Ben Bolker
2013-Jun-28 03:49 UTC
[Rd] problem with eval(..., parent.frame(1L)) when package is not loaded
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 The lmer() function in the lme4 package has some code of the form mc <- match.call() mc[[1]] <- as.name("lFormula") lmod <- eval(mc, parent.frame(1L)) this is a fairly common idiom in R, found e.g. in lm(), used when one wants to pass all of the arguments of a function to a different function (in the case of lm() it's model.frame()). This idiom fails ("error in eval(...) : could not find function 'lFormula'") when lme4 is not explicitly loaded, as will happen if one uses lme4::lmer(...) to call the function without loading the package, or when lmer() is called from within a function that Imports: but does not Depend: on lme4. A simpler version suggested by Martin Maechler shows that this is a generic issue (except that it's unlikely to happen with base R functions because the relevant packages _will_ be loaded unless one bends over backwards): R_DEFAULT_PACKAGES=NULL R --vanilla> stats::lm(y~x,data=data.frame(1:5,1:5))Error in eval(expr, envir, enclos) : could not find function "model.frame" I wonder if anyone has encountered this and/or can think of a fix (something with setting the enclosing environment appropriately?) The only workarounds we can think of at present are (1) telling dependent-package maintainers that they have to use Depends: instead of Imports: (which seems to subvert the whole dependency-minimization goal of Imports:) or (2) littering our code with lme4:: to make sure that the function gets looked for in the right place (ugh). See also github.com/lme4/lme4/issues/50 . Any ideas appreciated. Ben Bolker -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) Comment: Using GnuPG with undefined - enigmail.net iQEcBAEBAgAGBQJRzQfZAAoJEOCV5YRblxUH3d0H+wfIa3ZJICkgt+4O6hsCPf3h uMs5aQqZZl37W+8vOx4P6FBYb2dc96SGZhYmyYude9wokHnCx4E+GI34YJkASST0 v8BXp7kdk2kSXS+FBzwbxbIaUFlS44nzv/fA0zaVfa1YbsfWQaxKF2p/BLu+gxK8 pCdVWcTqN6rLsO2i9nPGG90W4zCo3mVx/83D4vbSXo17bYMuSrOd5OGUE9NRJz2G gJLqtH9DC+NbCbAO47Sa1/od7ABiRD2Cl8yN+fmNHQOiXYB50IrCA8kCNk10zNnT lZ/u90TPS2gHV0dF9MYPGgjp8GYpycXzdVt9u4idLOtYOOS7lBA5792jc0wrGoU=oBtv -----END PGP SIGNATURE-----
Duncan Murdoch
2013-Jun-28 10:34 UTC
[Rd] problem with eval(..., parent.frame(1L)) when package is not loaded
On 13-06-27 11:49 PM, Ben Bolker wrote:> -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > > The lmer() function in the lme4 package has some code of the form > > mc <- match.call() > mc[[1]] <- as.name("lFormula") > lmod <- eval(mc, parent.frame(1L)) > > this is a fairly common idiom in R, found e.g. in lm(), used when > one wants to pass all of the arguments of a function to a different > function (in the case of lm() it's model.frame()). > > This idiom fails ("error in eval(...) : could not find function > 'lFormula'") when lme4 is not explicitly loaded, as will happen if one > uses lme4::lmer(...) to call the function without loading the package, > or when lmer() is called from within a function that Imports: but does > not Depend: on lme4. > > A simpler version suggested by Martin Maechler shows that this is a > generic issue (except that it's unlikely to happen with base R > functions because the relevant packages _will_ be loaded unless one > bends over backwards): > > R_DEFAULT_PACKAGES=NULL R --vanilla >> stats::lm(y~x,data=data.frame(1:5,1:5)) > Error in eval(expr, envir, enclos) : > could not find function "model.frame" > > I wonder if anyone has encountered this and/or can think of a fix > (something with setting the enclosing environment appropriately?) The > only workarounds we can think of at present are (1) telling > dependent-package maintainers that they have to use Depends: instead > of Imports: (which seems to subvert the whole dependency-minimization > goal of Imports:) or (2) littering our code with lme4:: to make sure > that the function gets looked for in the right place (ugh). > > See also github.com/lme4/lme4/issues/50 . > > Any ideas appreciated.I believe mc[[1]] <- lFormula will mostly solve the problem. Since your code is evaluating lFormula, it will be found. This will likely lead to ugly error messages if errors occur during lFormula (because the call will no longer look like lFormula(...), it will look like (function(...) { ... })( ... ). You can mitigate this by using call. = FALSE in your error messages. If you're saving mc in any of the results of the function, you'll also see the ugly display. The other choice is to use the explicit ::, i.e. mc[[1]] <- quote(lmer::lFormula) This would be the solution I'd prefer, but you seem to have some prejudice against :: :-). Duncan Murdoch
Peter Meilstrup
2013-Jun-28 10:34 UTC
[Rd] problem with eval(..., parent.frame(1L)) when package is not loaded
On Thu, Jun 27, 2013 at 8:49 PM, Ben Bolker <bbolker@gmail.com> wrote:> -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > > The lmer() function in the lme4 package has some code of the form > > mc <- match.call() > mc[[1]] <- as.name("lFormula") > lmod <- eval(mc, parent.frame(1L)) > > this is a fairly common idiom in R, found e.g. in lm(), used when > one wants to pass all of the arguments of a function to a different > function (in the case of lm() it's model.frame()). >I think the use of stack inspection ( usually parent.frame(), but with match.call() often playing an accessory role) should be more widely considered Evil, but I could get very off topic on that. Passing along all the current function's arguments to another function is implemented fairly well in S3 method dispatch. For example: ### aMethod <- function(...) UseMethod("aMethod") aMethod.default <- function(object, continue) { if (continue) { .Class <- "alternate" NextMethod() } c("default", object) } aMethod.alternate <- function(object, continue) { c("alternate", object) } x <- "this is the wrong x" local({ x <- "this is the right x" aMethod(x, TRUE) }) ### Peter [[alternative HTML version deleted]]
Peter Meilstrup
2013-Jun-29 03:50 UTC
[Rd] problem with eval(..., parent.frame(1L)) when package is not loaded
On Fri, Jun 28, 2013 at 3:07 PM, Ben Bolker <bbolker@gmail.com> wrote:> On 13-06-28 03:57 PM, Peter Meilstrup wrote: > > Well, once you begin messing with eval.parent(), you _are_ telling R > > what environment to work in, and things have the potential to go wrong > > at that point because you're telling R to work in environment you don't > > know anything about. Having to be explicit with a :: is just > > compensating for an earlier thing you "shouldn't need to" be doing with > > environments (in my opinion.) > > What would your preferred idiom be for this task (calling another > function with all of the arguments of the previous call, without > uglifying the call in the process)? I read your e-mail about making the > comparison with S3 method dispatch, but to be honest I don't understand > it very well -- don't know if those particular techniques are applicable > here ... ? > > Ben >In the case of lmer you could do this: delegate <- structure(NULL, class="delegate") #' @S3method glmer delegate glmer.delegate <- glmer lmer <- function (formula, data, family = NULL, blahblahblah, ...) { if (!is.null(family)) { UseMethod("glmer", delegate) } #... } ### Peter [[alternative HTML version deleted]]