In this introduction to functional programming, the author Hadley Wickham creates the following function factory:
power <- function(exponent) {
function(x) {
x ^ exponent
}
}
He then shows how this function can be used to define other functions, such as
square <- power(2)
cube <- power(3)
Now suppose I wanted to create these functions simultaneously via the following loop:
ftns <- lapply(2:3, power)
This doesn't seem to work, as 3 gets assigned to the exponent for all entries of the list:
as.list(environment(ftns[[1]]))
$exponent
[1] 3
Can someone please help me understand what's wrong with this code?
Thanks!
What you're seeing is a consequence of R's use of promises to implement lazy argument evaluation. See Promise objects.
The problem is that in the power()
function the exponent
argument is never evaluated, meaning the underlying promise is never called (at least not until the generated function is evaluated).
You can force the promise to be evaluated like this:
power <- function(exponent) { exponent; function(x) x^exponent; };
ftns <- lapply(2:3,power);
sapply(ftns,function(ftn) environment(ftn)$exponent);
## [1] 2 3
Without the exponent;
statement to force evaluation of the promise, we see the issue:
power <- function(exponent) { function(x) x^exponent; };
ftns <- lapply(2:3,power);
sapply(ftns,function(ftn) environment(ftn)$exponent);
## [1] 3 3
Aha! The R changelog shows that this behavior was changed in 3.2.0:
* Higher order functions such as the apply functions and Reduce() now force arguments to the functions they apply in order to eliminate undesirable interactions between lazy evaluation and variable capture in closures. This resolves PR#16093.
It's classified as a new feature.
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