-1

Do you have to explicitly define the environment of a custom function in R, or is it automatically "created" (defined?) when a function is created?

I want to know if creating a function automatically creates an environment within said function or whether the environment needs to be explicitly created. If the former is true (i.e., the environment is automatically created), how do I explicitly refer to it?

Must I first create a new environment for said function (i.e., by assigning a new environment using new.env) or can I simply use some other syntax (perhaps something like "current.env()")?

The only simple example I can come up with that would use this is assign functions inside the function.

function(a,b) {
  assign(paste(a,b,sep='.'), 'help me', envir = ONLY CURRENT FUNCTION)
}
theforestecologist
  • 4,667
  • 5
  • 54
  • 91
  • 3
    I suspect a small code example could help illustrate what you are trying to do here - could you? – Spacedman Jan 05 '17 at 17:51
  • You can use the `get()` function; the first argument is the string name of the variable and the named 'envir' argument is the env you want to query.... `get('a', envir=my.env)` – BadZen Jan 05 '17 at 17:52
  • 2
    I think if you don't worry about it, things will work just fine. `for` doesn't have it's own environment or scope. – Gregor Thomas Jan 05 '17 at 17:53
  • @BadZen ok. But how do I select the environment of the function I'm working in as `my.env`? In other words how do I reference the environment of the function that `get` is in? – theforestecologist Jan 05 '17 at 17:55
  • 3
    Why do you want to select the environment of the function you are in? You're already in it. You'll just get things by name. Please try and add a code example to your question to show what the problem is. – Spacedman Jan 05 '17 at 17:56
  • `environment()` returns the lexically-current env – BadZen Jan 05 '17 at 17:56
  • @Spacedman - In R, environments/lexical scopes are first-class objects. You can pass them around like Javascript contexts and do awful call-by-name stuff. It's a whole thing. =) – BadZen Jan 05 '17 at 17:56
  • You probably shouldn't be using assign at all. – Roland Jan 05 '17 at 18:01
  • You should provide a minimal [reproducible example](http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) that makes it clear what problem you are trying to solve. It sounds like there are probably better ways to write your R code then the way you are pursuing. – MrFlick Jan 05 '17 at 18:02
  • @MrFlick I don't know how to do it, which is why I can only provide a non-working example to illustrate my question. – theforestecologist Jan 05 '17 at 18:03
  • 1
    @theforestecologist Well, you should have some idea of what you want your input and output to be. Why do you think you need references to particular scopes? What code did you try that didn't work, and how are you hoping that it would work. Also, don't change your question to ask new questions. Start a new post if you have a different issue. But i'm worried you are simply headed down the wrong path. – MrFlick Jan 05 '17 at 18:05
  • Fine Fine...Let me start over. I just want to know if creating a function automatically creates an environment within said function or whether the environment needs to be explicitly created. If the former is true (i.e., if the environment is automatically created), how do I *explicitly* refer to it? – theforestecologist Jan 05 '17 at 18:08
  • 3
    A function doesn't create an environment when it's defined (but it is defined in the context of an environment). A new environment is created with the function runs. And that can be returned with `environment()` from within the function. Buy why do you think you need that? It still isn't clear. – MrFlick Jan 05 '17 at 18:10
  • 1
    All user-defined functions are closures. There is always an environment associated with them. – Roland Jan 05 '17 at 18:12
  • @MrFlick. I need it for two reasons. The first, is to just simply try to understand how it works. The second, is to expand that new understanding to applying further applications of scope. The second question I asked in this question (which I've since deleted) is how do I refer to the current *within*-function environment *concurrently* with the function's parent environment? – theforestecologist Jan 05 '17 at 18:13

2 Answers2

7

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.

Gavin Simpson
  • 170,508
  • 25
  • 396
  • 453
  • THANK YOU. This is the exact *simple* explanation I was looking for. This thoroughly answers all of my questions. – theforestecologist Jan 05 '17 at 18:28
  • 2
    @theforestecologist You could do a lot worse than read Hadley Wickham's account of Environments from his Advanced R book, which has a free-to-read web version: http://adv-r.had.co.nz/Environments.html – Gavin Simpson Jan 05 '17 at 18:36
  • Thanks for the reference! Also, could you help me out with an extension of this question (see chat: http://chat.stackoverflow.com/rooms/132428/room-for-theforestecologist-and-gavin-simpson) – theforestecologist Jan 05 '17 at 19:46
0

Don't mention an environment in the assign.

 foo = function(a,b){
  assign(paste(a,b,sep="."),99)
 return(hey.you) # note this is hard coded here...
}

There's nothing called hey.you:

> ls()
[1] "foo"

I call foo, which creates a hey.you and returns its value:

> foo("hey","you")
[1] 99

But doesn't mess up my environment because it was created in the function's environment:

> ls()
[1] "foo"

because that's the default.

?assign

    pos: where to do the assignment.  By default, assigns into the
          current environment.  See ‘Details’ for other possibilities.

If you HAD to mention it explicitly, and you don't have to, so don't, unless you can give us a real good reason why you have to, then just copy what assign does:

> foo = function(a,b){assign(paste(a,b,sep="."),99,envir=as.environment(-1));ls()}
> foo("fnord","fnord")
[1] "a"           "b"           "fnord.fnord"

Or copy what everyone else has been saying here:

> foo = function(a,b){assign(paste(a,b,sep="."),99,envir=environment());ls()}
> foo("bubble","gum")
[1] "a"          "b"          "bubble.gum"
Spacedman
  • 92,590
  • 12
  • 140
  • 224
  • but if I HAD to reference the current environment of the function, how would I do that? – theforestecologist Jan 05 '17 at 18:04
  • Why isn't `environment()` doing what you want? – BadZen Jan 05 '17 at 18:07
  • Just copy what assign does: `assign(paste(a,b,sep="."),99,envir=as.environment(-1))` – Spacedman Jan 05 '17 at 18:08
  • @BadZen I don't fully understand how to use `environment()`. Would I just use `assign(paste(a,b,sep='.'), envir = environment())`? – theforestecologist Jan 05 '17 at 18:17
  • 1
    @theforestecologist for your purposes, you just use `environment()` like that, without any arguments. `environment()` does have 1 argument, `fun` which defaults to `NULL`, which allows other behaviour, such as to look up the environment of the function, it's closure - try `environment(lm)` for example - but you don't need that for the cases you cover in the question. – Gavin Simpson Jan 05 '17 at 18:26
  • @theforestecologist you keep forgetting to put a `value` in your example assign functions. – Spacedman Jan 05 '17 at 18:49
  • oops. you're right. sorry I was distracted by the environment part that I kept forgetting since it's trivial to the question I'm asking... :p – theforestecologist Jan 05 '17 at 18:53