melatonin15
melatonin15

Reputation: 2269

Name masking in R function - Advanced R by Hadley

I came across this example in Advanced R by Hadley. My question is after defining the function, j(1) outputs the inner function definition as supposed to what j(1)() is outputting? Intuitively, I think j(1) should output [1] 1 2

Could anyone explain what's going on actually? What's the difference between j(1) and j(1)() ?

> j <- function(x) {
+   y <- 2
+   function() {
+     c(x,y)
+   }
+ }

> k <- j(1)

> k()
[1] 1 2

> j(1)
function() {
    c(x,y)
  }
<environment: 0x7fa184353bf8>

> j()
function() {
    c(x,y)
  }
<environment: 0x7fa18b5ad0d0>

> j(1)()
[1] 1 2

Upvotes: 2

Views: 705

Answers (1)

Rich Scriven
Rich Scriven

Reputation: 99331

tl;dr In R, the return value of a function can also be a function. That's the case here. j(1) returns a function, whereas j(1)() returns a numeric vector.


The difference between j(1) and j(1)() is that j(1) outputs a function because that's the last value in the definition of j. Functions return their last expression (or the value found in a relevant return() call), which in this case is also a function. j(1)() is calling the last value of j, which is the function returned from it. It does not take an argument, so the empty parentheses () is the argument list for j(1)

It might become a bit more clear if we have a closer look at j and some of its properties.

j <- function(x) {
    y <- 2
    function() {
        c(x, y)
    }
}

The difference between the calls becomes quite apparent when we look at their classes.

class(j(1))
# [1] "function"
class(j(1)())
# [1] "numeric"

When you defined j, 2 is hard-coded into its return function as the second value of the vector returned from that function. We can see the precise return value of a call to j(1) with

library(pryr)
unenclose(j(1))
# function () 
# {
#     c(1, 2)
# }

So a call to j(1)() (or k()) will deliver the vector c(1, 2). Similarly, if we call j(5), the return value of j(5)() is c(5, 2)

unenclose(j(5))
# function () 
# {
#     c(5, 2)
# }

Hope that helps.

Credit to @Khashaa for mentioning the unenclose() function (comment deleted).

Upvotes: 7

Related Questions