Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where are function constants stored if a function is created inside another function?

Tags:

r

I am using a parent function to generate a child function by returning the function in the parent function call. The purpose of the parent function is to set a constant (y) in the child function. Below is a MWE. When I try to debug the child function I cannot figure out in which environment the variable is stored in.

power=function(y){
  return(function(x){return(x^y)})
}

square=power(2)

debug(square)

square(3)

debugging in: square(3)
debug at #2: {
    return(x^y)
}

Browse[2]> x
[1] 3
Browse[2]> y
[1] 2
Browse[2]> ls()
[1] "x"
Browse[2]> find('y')
character(0)
like image 904
ak17 Avatar asked May 22 '26 16:05

ak17


1 Answers

If you inspect the type of an R function, you’ll observe the following:

> typeof(square)
[1] "closure"

And that is, in fact, exactly the answer to your question: a closure is a function that carries an environment around.

R also tells you which environment this is (albeit not in a terribly useful way):

> square
function(x){return(x^y)}
<environment: 0x7ffd9218e578>

(The exact number will differ with each run — it’s just a memory address.)

Now, which environment does this correspond to? It corresponds to a local environment that was created when we executed power(2) (a “stack frame”). As the other answer says, it’s now the parent environment of the square function (in fact, in R every function, except for certain builtins, is associated with a parent environment):

> ls(environment(square))
[1] "y"
> environment(square)$y
[1] 2

You can read more about environments in the chapter in Hadley’s Advanced R book.


Incidentally, closures are a core feature of functional programming languages. Another core feature of functional languages is that every expression is a value — and, by implication, a function’s (return) value is the value of its last expression. This means that using the return function in R is both unnecessary and misleading!1 You should therefore leave it out: this results in shorter, more readable code:

power = function (y) {
  function (x) x ^ y
}

There’s another R specific subtlety here: since arguments are evaluated lazily, your function definition is error-prone:

> two = 2
> square = power(two)
> two = 10
> square(5)
[1] 9765625

Oops! Subsequent modifications of the variable two are reflected inside square (but only the first time! Further redefinitions won’t change anything). To guard against this, use the force function:

power = function (y) {
  force(y)
  function (x) x ^ y
}

force simply forces the evaluation of an argument name, nothing more.


1 Misleading, because return is a function in R and carries a slightly different meaning compared to procedural languages: it aborts the current function exectuion.

like image 149
Konrad Rudolph Avatar answered May 25 '26 06:05

Konrad Rudolph



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!