First Aid for Closures

by Karl-Kuno Kunze

My Article about Closures is by far the most frequently viewed article in the blog. The reason for this is probably the somewhat cryptic error message  object of type 'closure' is not subsettable, which R sends to you sometimes. Here is a three step first-aid plan.

Immediate help

If you want to continue your work with the least detour possible:

Replace the word closure with the word function in the error message.

Good luck with your debugging efforts and see you soon!

In somewhat more detail

If you have a minute or two or didn’t trace down the bug you might find it interesting to see how we can provoke the error message on purpose. To that end, we define a function and call it correctly in the first case but then interchange the round brackets by square brackets. R does not like this:

a <- function() {return(42)}
## [1] 42
## Error in a[1]: object of type 'closure' is not subsettable

You treat a like a vector, which you can subset with square brackets to access the first element. That is bound to fail. Either you have used a function by error or you chose the square bracket by error – which is a method to subset an object.

In even more detail

R is a functional language. A functional language treats functions like first class citizens. Nobody prevents you from defining a function that returns itself a function to the caller. We could call this a fun(ction) factory. Let us look at an example:

funFactory <- function () {
    function (var) {var + 1} 
inc <- funFactory()
## [1] 3

Now, let’s push it a little further and get a little closer to a grasp of what a closure is. Let us change the previous example a little:

funFactory <- function (input) {
  var <- input
    function () {var + 1} 

The function incFactory accepts an argument and assigns it to the variable var. However, the function which we return does not accept any arguments. It looks for the variable in the next available environment, which is the environment of incFactory. Therefore the function does not produce an error which we should somehow expect. The object which was found this way is then incremented by one – therefor the cool name inc for increment.

Now we assign to object inc2 the function, that incFactory returns. The function incFactory expects an argument which we happily provide.

inc2 <- funFactory(2)
## [1] 3

When calling the function inc2 with inc2() we don’t just receive an error message but inc2 took its  Environment along when it left the factory! This environment is searched and the 2 is found such that the function can return the value of 3.

And this is what a closure is:

Function + Environment = Closure

In a functional language a function never lives alone, but always in conjunction with its surrounding environment.

A function always has its suitcase with environments with it when travelling. Hovewer, the suitcase is packed only once: upon creation in the factory!

Here a small test:

inc20 <- funFactory(20)
## [1] 21

Perhaps now you have the nerves to read through the other article about Closures and Environments. Have fun playing around!

Cover Picture: Rainer Sturm  /