Anonymous Functions in R - Part 1

An Anonymous Function (also known as a lambda experssion) is a function definition that is not bound to an identifier. That is, it is a function that is created and used, but never assigned to a variable.

Base R anonymous function syntax

An example in R where anonymous functions are used is in *apply() family of functions. In the following example a function is defined which takes one argument, adds one to it, and then returns it.

sapply(1:2, function(x) x + 1L)
[1] 2 3

Note that this is everything needed for a function definition (formal arugments, a body). It is never actually assigned to a variable, but used immediately by sapply() on each element of the given vector.

purrr’s anonymous function syntax

The purrr package uses the rlang package to help define anonymous functions in a very brief way.

Instead of having to type function(x) {...}, a function is constructed from a user-specified, one-sided formula. Because purrr/rlang enforces some constraints on the type of arguments the function definition is very abbreviated:

purrr::map_int(1:2, ~.x + 1L)
[1] 2 3

In the above code, the one-sided formula ~.x + 1 is expanded by rlang::as_function() into a full function which looks something like:

function(.x) {
  .x + 1
}

The advantage of purrr’s syntax is that it’s very brief. It allows the user to define functions quickly and with a minimum of characters by:

  1. Not having to specify function()
  2. Pre-defining the arguments to the function to be .x (and maybe .y and some other things), and not giving the user any option to change the defaults.

My issue with purrr’s anonymous function syntax

I realised recently, that while I like the brevity of the anonymous function syntax in purrr/rlang I felt it a little too constraining because the two argument names can only be .x and .y. This can be a problem as it can limit the readability of the function quite a bit.

For example, if mapping over vectors of volumes and surface_areas, then a map2 call might look like:

ratios <- purrr::map2(volumes, surface_areas, ~.x/.y)

Looking at the anonymous function in isolation, ~.x/.y isn’t really that informative on what we are calculating, and it would be nice if we could instead call something like:

ratios <- purrr::map2(volumes, surface_areas, ~vol/area)

Alternatives to purrr’s anonymous function syntax

I’ve since been on a hunt for an alternative syntax that allows for named arguments while still retaining some of the brevity of the purrr/rlang implementation.

I’ve listed all the ones I could find below, and in a future post I will try and compare them to find what syntax is possible and/or useful within the realms of R. Stay tuned…