I am trying to use get
in series of function calls, but the lookup of the object names seems to skip environments. For example:
foo <- 1 # variable in .GlobalEnv
getter <- function(x) {get(x)}
getter("foo") # returns 1, which is expected
f1 <- function() {
foo <- 2 # local variable in the function scope
getter("foo")
}
f1() # still returns 1, would've expected to return 2
Why is it that calling f1
returns the foo
in the global environment and not the foo
in the calling function's environment?
How do I have get
look in the calling function's environment? Setting pos = sys.parent()
does not seem to work.
When a function call is made, function's arguments are PUSHed on stack. These arguments are further referenced by base pointer. When the function returns to its caller, the arguments of the returning function are POPed from the stack using LIFO method.
R keeps track of active function calls using a call stack comprised of stack frames. Only global variables and variables in the current stack frame can be accessed directly.
Environment can be thought of as a collection of objects (functions, variables etc.). An environment is created when we first fire up the R interpreter. Any variable we define, is now in this environment. The top level environment available to us at the R command prompt is the global environment called R_GlobalEnv .
A call stack is a mechanism for an interpreter (like the JavaScript interpreter in a web browser) to keep track of its place in a script that calls multiple functions — what function is currently being run and what functions are called from within that function, etc.
You are being tripped up by the subtle differences between frames and environments (which is even more subtle since frames are environments, or maybe environments are frames) and the difference between lexical and dynamic scoping. There are some details in the help page for parent.frame
and other places spread across various documentation.
To try and simplify:
Your getter
function has its own environment where variables local to that function are stored (x
in this case). Since R is lexically scoped that means that the functions environment has a parent environment which is defined by where the function is defined, the global environment in this case (if it were defined inside of another function then the parent environment would be the env for that function).
When you call f1
and it calls getter
then getter tries to find the variable foo
, it first looks in its own environment, does not find it there, then looks in its parent environment which is the global env and finds foo
with the value of 1.
Your thinking goes along the lines of dynamic scoping, which the frames approximate. When f1
is called it gets its own environment (within which foo
will be assigned the value 2), then it calls the getter
function. The environment of foo
is not the parent of getter
's env (lexical scoping), but the environment of f1
is the parent frame of getter
since getter
was called from f1
, so to look in the environment of f1
you need to tell the get
function to look in the parent frame rather than the parent environment.
The summary of this is that the parent environment is the environment where a function was defined (lexical scoping), the parent frame is the frame/environment from which the function was called (simulated dynamic scoping).
If you define getter
to look in the parent frame, it works:
getter <- function(x) get(x, envir=parent.frame())
Then:
getter("foo")
[1] 1
f1()
[1] 2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With