The current environment is returned by environment()
:
> environment()
<environment: R_GlobalEnv>
> foo <- function() environment()
> foo()
<environment: 0x1391b88>
> foo()
<environment: 0x1391f08>
> foo()
<environment: 0x1392288>
> foo()
<environment: 0x13916a0>
Notice that each call to foo()
has a different environment as the environment you are thinking about only gets created when the function is called.
To get the enclosing frame, where the function was called from, use parent.frame()
:
> bar <- function() parent.frame()
> bar()
<environment: R_GlobalEnv>
As bar()
was called from my workspace, the global environment is returned. But, if we call bar()
from inside another function, we get the environment of that function as that is now the calling environment:
> foobar <- function() { print(environment()); bar() }
> foobar()
<environment: 0x74a7d68>
<environment: 0x74a7d68>
If we look at your assign()
example, you don't have to tell it which environment to use if you want the current environment as that is how it is setup to work by default. But, if you wanted to be explicit about this you'd use:
foo <- function(x) {
assign("y", x, envir = environment())
y # return(y) to show it is assigned `x`
}
Which gives
> foo(20)
[1] 20
> foo(10)
[1] 10
> foo("a")
[1] "a"
If you want to assign in the parent frame, just pass envir = parent.frame()
:
foo <- function(x) {
assign("y", x, envir = parent.frame())
"not returning `y`" # return something else
}
which gives
> ls()
[1] "bar" "foo" "foobar"
> foo(20)
[1] "not returning `y`"
> ls()
[1] "bar" "foo" "foobar" "y"
> y
[1] 20
Even though we didn't return y
, the assign()
created y
in the environment we specified.
It is important to stress though that rarely should one be doing assign()
like this, to assign into other environments. A major reason for this is that now your function has side effects and R is a functional language where it is easier to reason about how code works by having a function take inputs and return outputs, without affecting anything else. Hence you would prefer
foo1 <- function(x) {
y <- x
y
}
y <- foo1(x)
over
foo2 <- function(x, varname) {
assign(varname, x, envir = parent.frame())
invisible()
}
foo2(x, "y")
because I know that calling foo1()
has no side effects, it's just taking some inputs and returning an output. This is a little contrived because you chose assign()
but this would apply broadly. It's also the reason why complex lists or other types of objects are returned from many R functions, especially those fitting a model.