[Rd] Environment when NextMethod is used

Hadley Wickham
Tue, 04 Sep 2012 02:50:57 -0000

Any ideas? Is this a big or a deliberate feature?
Hadley

On Saturday, September 1, 2012, Winston Chang wrote:

> I'm running into some hard-to-understand behavior with the evaluation
> environment when NextMethod is used. I'm using square-bracket indexing
> into objects, and the evaluation environment of the expression inside
> the square brackets seems to change depending on what kind of
> comparison operators are used.
>
> This behavior happens when the following conditions are met (this is
> what I've found; I'm sure that these aren't necessary and
sufficient
> conditions):
> - I call a function from an attached package.
> - The function uses square bracket indexing with a class that has its
> own definition of the operator, such as `[.factor` or `[.POSIXct`. (If
> a vector of numerics is used, the error doesn't happen.)
> - The indexing function uses NextMethod("[").
> - An S3 method is used within the square brackets. (When a regular
> function is used, there's no error.)
> - The S3 method is from a package that is an import for the original
> function's package, but this package is not attached. (If the package
> is attached, then the error doesn't happen because R finds the method
> in the standard search path.)
> - An operator like == is used. (If the %in% operator is used, the
> error doesn't happen.)
>
>
> This may sound very abstract. I've created a sample package that
> illustrates the behavior. The package is called envtest, and it has a
> function called envtest(), which uses an S3 method from the nlme
> package. nlme is listed as an import.
>
> You can either clone the repository here:
>   https://github.com/wch/envtest
> Or you can install it with devtools, using:
>   library(devtools)
>   dev_mode()
>   install_github('envtest', 'wch')
>
>
>
> The envtest() function tries to index into a factor in different ways,
> and prints the output for each one. This is the content of the
> function. (If you load it from the global environment, it won't have
> the same error, since the issue has to do with an import):
>
> envtest <- function() {
>   dat <- data.frame(x = 0, y = 0)
>   f <- factor(c("a", "b"))
>
>   # Print the starting data
>   cat("\nf                                                : ")
>   cat(f)
>
>   cat("\n\nTests with %in% operator
----------------------------")
>
>   # OK
>   cat('\n"x" %in% Names(y ~ x, data = dat)                :
')
>   cat("x" %in% Names(y ~ x, data = dat))
>
>   # OK: Save boolean values to idx, then use f[idx]
>   cat('\nidx <- "x" %in% Names(y ~ x, data = dat); f[idx]
: ')
>   cat({idx <- "x" %in% Names(y ~ x, data = dat); f[idx]})
>
>   # OK: Use the expression with S3 function Names directly inside of []
>   cat('\nf["x" %in% Names(y ~ x, data = dat)]             :
')
>   cat(f["x" %in% Names(y ~ x, data = dat)])
>
>
>   cat("\n\nTests with == operator
------------------------------")
>
>   # OK
>   cat('\n"x" == Names(y ~ x, data = dat)                  :
')
>   cat("x" == Names(y ~ x, data = dat))
>
>   # OK: Save boolean values to idx, then use f[idx]
>   cat('\nidx <- "x" == Names(y ~ x, data = dat); f[idx]
: ')
>   cat({idx <- "x" == Names(y ~ x, data = dat); f[idx]})
>
>   # Error: Use the expression with S3 function Names directly inside of []
>   cat('\nf["x" == Names(y ~ x, data = dat)]               :
')
>   cat(f["x" == Names(y ~ x, data = dat)])
>
>   invisible()
> }
>
>
>
> This is what happens when I run the envtest() function. All the
> indexing operations work, except the last one, where, inside the
> square brackets, the == operator is used, and it calls the S3 method
> from an imported package.
>
> > library(envtest)
> > envtest()
> f                                                : 1 2
>
> Tests with %in% operator ----------------------------
> "x" %in% Names(y ~ x, data = dat)                : TRUE
> idx <- "x" %in% Names(y ~ x, data = dat); f[idx] : 1 2
> f["x" %in% Names(y ~ x, data = dat)]             : 1 2
>
> Tests with == operator ------------------------------
> "x" == Names(y ~ x, data = dat)                  : FALSE TRUE
> idx <- "x" == Names(y ~ x, data = dat); f[idx]   : 2
> f["x" == Names(y ~ x, data = dat)]               : Error in
Names(y ~
> x, data = dat) : could not find function "Names"
>
>
> When I set options(error=recover), it's possible to investigate the
> environment where it's trying to evaluate the expression Names(....),
> when it runs into the error:
>
> Enter a frame number, or 0 to exit
>
> 1: envtest()
> 2: envtest.r#40: cat(f["x" == Names(y ~ x, data = dat)])
> 3: f["x" == Names(y ~ x, data = dat)]
> 4: `[.factor`(f, "x" == Names(y ~ x, data = dat))
> 5: NextMethod("[")
> 6: Names(y ~ x, data = dat)
>
> Selection: 5
>
> Browse[1]> environment()
> <environment: 0x104421a78>
> Browse[1]> parent.env(environment())
> <environment: namespace:base>
> Browse[1]> parent.env(parent.env(environment()))
> <environment: R_GlobalEnv>
>
>
> When == is used, it tries to evaluate the expression Names(....) in
> the environment namespace:base, and it fails because it can't find the
> function.
> However, when %in% is used, it tries to evaluate the expression
> Names(....) in the environment namespace:nlme, which makes more sense
> to me.
>
>
> Is this expected behavior? And if so, could someone explain why it
> should be expected? I'm confused as to why the evaluation environment
> should change when a certain narrow set of conditions is met.
>
>
> -Winston
>
> ______________________________________________
> R-devel@r-project.org <javascript:;> mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>


--
Assistant Professor
Department of Statistics / Rice University
http://had.co.nz/

	[[alternative HTML version deleted]]