Functions Are Objects

If you’ve used R for any length of time, you would have likely come across this statement;

Functions are objects.

I was reading the book Advanced R the other day and Hadley Wickham was saying it again. Then somehow it dawned on me that functions really are objects!

So, what is an object in R? According to the R manual An Introduction to R, which I think no one ever reads, an object is simply an entity upon which R operates. The document went ahead to cite atomic vectors as examples. In code, that’s

# Some atomic vectors
x <- 1:8
y <- c("apples", "grow", "on", "trees")
z <- runif(10)
## [1] 1 2 3 4 5 6 7 8
## [1] "apples" "grow"   "on"     "trees" 
##  [1] 0.10622862 0.86598075 0.11847392 0.49576064 0.06002724 0.79476014
##  [7] 0.86503160 0.33285702 0.52400112 0.42741101

Here, the operations underlying :, c and runif are creating objects that have been named x, y and z, respectively. We will (very) colloquially call them constructors—not necessarily in the sense of an object-oriented programming paradigm.

To create (i.e. define) a function in R, we have the function function (yes, tautology but it’s right). In the same spirit as the foregoing code, it creates or constructs a function object.

add_two <- function(arg) arg + 2

If we check the objects that are now in the workspace with objects or ls, we notice that our function is also there.

## [1] "add_two" "x"       "y"       "z"


Source: Wikimedia Commons

What was for me a kind of epiphany was the realization that a function is a data structure with different parts, pretty much like a list.

## $arg
## [[2]]
## arg + 2

The functions created by function are also called closures which have 3 major components:

  • formals, which is the argument list
  • body, which is the R code (or expression(s)) run when the function is called, and
  • environment
## $arg
## arg + 2
## <environment: R_GlobalEnv> 

After creating the function we can call it. It is at this stage that all the components kick in:

  • the formals have a pairlist of arguments which are key-value pairs with the key being the parameter’s name and the value is the actual argument supplied to it;
  • the body contains the expressions that are parsed by R, returning a value or generating a side-effect e.g. writing to a file; and
  • the environment that, simply put, is a container for the names bound to the different objects evaluated in the function (this is what mainly determines scope)

So when we call the function with one of the atomic vectors created earlier,

add_two(arg = x)
## [1]  3  4  5  6  7  8  9 10

we can see that add_two has added 2 to each element of the vector x.

Is this knowledge really important? For me, understanding that functions are objects just like others made it easier to reason about them when using them for programming. Additionally, when one ventures into functional programming or meta-programming, it makes these more advanced approaches a lot easier to grasp. But that’s a topic for another day.


Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s