Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does evaluating an expression in system.time() make variables available in global environment?

Tags:

scope

r

Can somebody please explain what happens when an expression is evaluated in system.time? In particular, why are any variables that are declared in the expr argument visible in the global environment?

Here is a pared-down version of the innards of system.time that does nothing other than evaluating the expression that is passed to the function:

st <- function(expr){
  expr
}

st(aa <- 1)
aa
[1] 1

Clearly the effect of this is that it creates the variable aa in the global environment. This baffles me, since I thought that assigning a variable inside a function makes it local in scope.

What happens here?

like image 882
Andrie Avatar asked Nov 01 '11 20:11

Andrie


People also ask

Can R functions access global variables?

Functions can access global variables and modify them. Modifying global variables in a function is considered poor programming practice.

How do you access a variable in a function in R?

Accessing Specific Variables in an Object Accessing a particular variable (i.e., column) in a data object is simple: DataObject$VarName , where DataObject is the data object and VarName the variable desired. The $ (dollar) symbol is how R links the requested variable to the data object.


2 Answers

It is because supplied arguments are evaluated in the evaluation frame of the calling function (as described in Section 4.3.3 of the R Language Definition document).

The expression wrapped by the user in system.time() is a supplied argument which gets matched positionally to expr. Then, when expr's evaluation is forced in the body of system.time, it is evaluated in the evaluation frame of the calling function. If system.time() was called from the .GlobalEnv, that is where any assignments that are a part of expr will take place.

EDIT:

Here's an example showing that expr is evaluated in the global environment if it is a supplied (but not a default) argument.

st2 <- function(expr = newVar <- 33){
   expr
}

# Using the default argument -- eval and assignment 
# within evaluation frame of the function. 
st2()
newVar
Error: object 'newVar' not found

# Using a supplied argument -- eval and assignment
# within the calling function's evaluation frame (here .GlobalEnv)
st2(newVar <- 44)
newVar
# [1] 44
like image 147
Josh O'Brien Avatar answered Oct 29 '22 11:10

Josh O'Brien


EDIT : as per @Tommy's comment: The evaluation actually only takes place once the argument expr is used (that's the lazy evaluation).

What is passed is a language object, not an expression. You basically nest the <- function (with two arguments) within the st() function call, and the result of the <- call is passed to to st. As you can see in ?assignOps, the <- function returns the assigned value silently. As @Josh told you already, this evaluation of the nested function takes place in the environment the function is called from.

What you do, is equivalent to

st(mean(1:10))

To see the difference, you can do:

st <- function(expr){
  typeof(expr)
}
> st(aa <- 1)
[1] "double"
> st(expression(aa <- 1))
[1] "expression"

For the structure of the call, you can do:

st <- function(expr){
  str(as.list(match.call()))
}
> st(mean(1:10))
List of 2
 $     : symbol st
 $ expr: language mean(1:10)
> st(aa <- 1)
List of 2
 $     : symbol st
 $ expr: language aa <- 1
like image 26
Joris Meys Avatar answered Oct 29 '22 09:10

Joris Meys