[R] Function in default parameter value closing over variables defined later in the enclosing function

Duncan Murdoch murdoch@dunc@n @end|ng |rom gm@||@com
Wed Jan 23 15:56:25 CET 2019


On 23/01/2019 4:53 a.m., Ivan Krylov wrote:
> Hi!
> 
> I needed to generalize a loss function being optimized inside another
> function, so I made it a function argument with a default value. It
> worked without problems, but later I noticed that the inner function,
> despite being defined in the function arguments, somehow closes over a
> variable belonging to the outer function, which is defined later.
> 
> Example:
> 
> outside <- function(inside = function() print(secret)) {
> 	secret <- 'secret'
> 	inside()
> }
> outside()
> 
> I'm used to languages that have both lambdas and variable declaration
> (like perl5 -Mstrict or C++11), so I was a bit surprised.

Defaults of variables are evaluated in the evaluation frame of the call.
So the inside() function is created in the evaluation frame, and it's 
environment will be that frame.

When it is called it will create a new evaluation frame (empty in your 
example), with a parent being its environment, i.e. the evaluation frame 
from when it was created, so it will be able to see your secret variable.

If it made an assignment to secret using standard "<-" assignment, it 
would create a new variable in its own evaluation frame, but if it used 
superassignment "<<-", it would modify the original secret variable.

> 
> Does this work because R looks up the variable by name late enough at
> runtime for the `secret` variable to exist in the parent environment of
> the `inside` function? Can I rely on it? Is this considered bad style?
> Should I rewrite it (and how)?

I would consider it bad style if the inside() function had anything 
other than a trivial definition as in your example.  However, in my 
opinion it would be fine to write it as

  outside <- function(inside = defaultInsideFn) {
     defaultInsideFn <- function() print(secret)
     secret <- 'secret'
     inside()
  }

which is essentially equivalent, other than having a shorter header on 
the outside() function.

Duncan Murdoch



More information about the R-help mailing list