R eval has misleading documentation for the case where the envir argument is a list or a pairwise list

I'm not sure if this should be posted on R-devel (if yes, just let me know ...), but there seems to be at least a bug in the documentation of the R function eval()

that is needed for the R Non Standard evaluation functions or what am I wrong?

> eval   
function (expr, envir = parent.frame(), enclos = if (is.list(envir) || 
    is.pairlist(envir)) parent.frame() else baseenv()) 
.Internal(eval(expr, envir, enclos))
<bytecode: 0x0000000009534280>
<environment: namespace:base>

      

This as well as this part eval

will help in the argumentenclos

> Relevant when envir is a (pair)list or a data frame. Specifies the
enclosure, i.e., where R looks for objects not found in envir. This can be
NULL (interpreted as the base package environment, baseenv()) or an
environment.

      

indicates that whenever a list is supplied as an argument eval()

envir

, the value enclos

is evaluated (lazily) on parent.frame()

, so it should be the same as supplying parent.frame()

extra (but this time itself). Note, however, that the next two code snippets where I only change this, do not return the same.

> rm(list = ls(all = TRUE))
> f1 <- function(){
   y <- 2
   f2 <- function(){
     eval(quote(y), envir = list())
   }
   f2
 }
> f3 <- f1()
> f3()
[1] 2

      

and

> rm(list = ls(all = TRUE))
> f1 <- function(){
   y <- 2
   f2 <- function(){
     eval(quote(y), envir = list(),
          enclos = parent.frame())
   }
   f2
 }
> f3 <- f1()
> f3()
Error in eval(expr, envir, enclos) : object 'y' not found 

      

So the only conclusion I can get from here is that apparently for data frames and lists, the default environment is not parent.env()

. Instead, R appears to be looking at the current runtime. Here's the only relevant part I can find in ?eval

about this:

> When evaluating expressions in a data frame that has been passed as an
argument to a function, the relevant enclosure is often the caller 
environment, i.e., one needs eval(x, data, parent.frame()).

      

Something indicates that you need to explicitly specify parent.frame()

. And that the above behavior really doesn't match eval()

by default ... (at least for the case when envir

set to a list / pair list).

This is very confusing for me. However, I'm not sure if there is anything wrong, and would appreciate any response or comment.

+3


source to share


1 answer


For this particular example, in the first case, the definition is f3

:

f3
#function(){
#     eval(quote(y), envir = list())
#   }
#<environment: 0x02a41584>

      

By default, the argument enclos = parent.frame()

will be evaluated in the evaluation frame (ie "inside") eval

- the parent of the current environment eval

is environment()

its current wrapper function ( f3

). And its wrapper function "remembers" where it was created and looks right y

to find it.

In the second case, it f3

is defined as:

f3
#function(){
#     eval(quote(y), envir = list(),
#          enclos = parent.frame())
#   }
#<environment: 0x02a4e5e4>

      

Here, enclos = parent.frame()

evaluates to environment()

of f3

and, by calling f3()

, its parent is .GlobalEnv

where not y

.

As a clearer example (still equally nested as an example), we might consider:

f0 = function(e) print(e)
f1 = function(e = parent.frame()) f0(e)
fA = function() #returns a function
{
    function() 
    { 
        print(parent.frame())
        print(environment())
        f1() 
    }
}   
fB = function() # returns a function
{
    function() 
    { 
        print(parent.frame())
        print(environment())
        f1(parent.frame()) 
    }
}   

      

And the call:

fA()()
#<environment: R_GlobalEnv>  #<- parent of `fA()` `environment()`
#<environment: 0x06ff25bc>   #<- current env of `fA()`
#<environment: 0x06ff25bc>   #<- parent of `f1` == current of `fA()`

fB()()
#<environment: R_GlobalEnv>  #<- parent of `fA()` `environment()`
#<environment: 0x06fec304>   #<- current env of `fA()`
#<environment: R_GlobalEnv>  #<- parent is `eval`ed as parent of `fA()` current

      



As Taz points out, the difference between default arguments and supplied arguments is stated in the manual. Fooling around a bit, we could see this in action trying to find the promises arguments:

First, a helper function to access the promises of the current arguments:

.ff = inline::cfunction(sig = c(symarg = "symbol", env = "environment", penv = "environment"), body = '
    SEXP arg = findVar(symarg, env), ans = allocVector(VECSXP, 5);
    SET_VECTOR_ELT(ans, 0, PRCODE(arg)); 
    SET_VECTOR_ELT(ans, 1, PRENV(arg));
    SET_VECTOR_ELT(ans, 2, eval(PRCODE(arg), PRENV(arg)));
    SET_VECTOR_ELT(ans, 3, env);
    SET_VECTOR_ELT(ans, 4, penv);
    return(ans);
')

      

And the function whose arguments we will track:

ff = function(arg = parent.frame())
{
    ans = setNames(.ff(quote(arg), environment(), parent.frame()), 
                   c("expr", "envir", "val", "cur", "par"))

    cat(sprintf("promise:\n\tcall: '%s'\n\tsearched at: '%s'\n\tfound as: '%s'\ncurrent: '%s'\nparent: '%s'\n%s\n", 
                deparse(ans$expr), capture.output(ans$envir), 
                capture.output(ans$val), capture.output(ans$cur),
                capture.output(ans$par), strrep("-", 40)))

    return(invisible(ans))
}

      

And a simple example:

ff()
#promise:
#        call: 'parent.frame()'
#        searched at: '<environment: 0x06fff594>'
#        found as: '<environment: R_GlobalEnv>'
#current: '<environment: 0x06fff594>'
#parent: '<environment: R_GlobalEnv>'
#----------------------------------------

ff(parent.frame())
#promise:
#        call: 'parent.frame()'
#        searched at: '<environment: R_GlobalEnv>'
#        found as: '<environment: R_GlobalEnv>'
#current: '<environment: 0x06fcce20>'
#parent: '<environment: R_GlobalEnv>'
#----------------------------------------

      

Or a more nested case:

fnest1 = function() ff()
fnest2 = function() ff(parent.frame())

fnest1()
#promise:
#        call: 'parent.frame()'
#        searched at: '<environment: 0x028d1ccc>'
#        found as: '<environment: 0x028d1d20>'
#current: '<environment: 0x028d1ccc>'
#parent: '<environment: 0x028d1d20>'
#----------------------------------------

fnest2()
#promise:
#        call: 'parent.frame()'
#        searched at: '<environment: 0x026866b8>'
#        found as: '<environment: R_GlobalEnv>'
#current: '<environment: 0x0268662c>'
#parent: '<environment: 0x026866b8>'
#----------------------------------------

      

+3


source







All Articles