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.

In a prior post I discussed how I am not completely satisfied with `purrr`

/`rlang`

’s
anonymous function syntax as it has no capability for naming the
function arguments anything other than `.x`

and `.y`

.

In this post, I’ll look at alternate syntaxes for creating anonymous functions.

## Implementations of anonymous functions in R

There are many implementations of helpers to create anonymous functions.

Base R has its own mechanism, and all the other implementations try and keep
the syntax shorter by dropping the requirement to actually type `function()`

when
creating the anonymous function.

The following table lists the implementations of anonymous functions I could find in R, and their syntax for creating an anonymous function with 2 arguments.

The **Formal Arguments** column indicates how the arguments for the anonymous
function are derived. Some packages support both implicit and explicit argument
naming (e.g. `gsubfn`

and `pryr`

). If the formal arguments are not explicitly stated,
then the argument list may be inferred by:

- convention e.g.
`rlang::as_function()`

assumes arguments are`.x`

and`.y`

- inference e.g.
`pryr::f()`

uses all variables found in the expression as formal arguments.

Package | Function Name | 1-arg | 2-args | Explicit Arg Names | Formal Arguments | Extra chars for implicit args | Core Syntax | Core Syntax Nchars | formals/body separator | comments | author | URL |
---|---|---|---|---|---|---|---|---|---|---|---|---|

base r | function | function(x) x + 1 | function(x, y) x + y | Yes | Explicit | NA | function() | 10 | NA | NA | R Core | NA |

rlang | as_function | as_function(~.x + 1)) | as_function(~.x+.y) | No | Implicit. Only .x or .y | .x | f(~) | 4 | NA | used within purrr | Lionel Henry et al | https://cran.r-project.org/package=rlang |

pryr | f (implicit args) | f(x + 1) | f(x + y) | No | Implicit. Inferred from expression | NA | f() | 3 | NA | NA | Hadley Wickham | https://cran.r-project.org/package=pryr |

pryr | f (explicit args) | f(x, x + 1) | f(x, y, x + y) | Yes | Explicit | NA | f() | 3 | , | NA | NA | NA |

nofrills | fn | fn(x ~ x + 1) | fn(x, y ~ x + y) | Yes | Explicit | NA | f(~) | 4 | ~ | Fully NSE aware (!!) | Eugene Ha | https://cran.r-project.org/package=nofrills |

gsubfn | as.function.formula (implicit args) | as.function.formula(~ x + 1) | as.function.formula(~ x + y) | No | Implicit. Inferred from RHS of formula | NA | f(~) | 4 | NA | NA | G. Grothendieck | https://cran.r-project.org/package=gsubfn |

gsubfn | as.function.formula (explicit args) | as.function.formula(x ~ x + 1) | as.function.formula(x + y ~ x + y + z) | Yes | Explicit. Inferred from LHS of formula | NA | f(~) | 4 | ~ | NA | NA | NA |

wrapr | lambda | lambda(x, x + 1) | lambda(x, y, x + y) | Yes | Explicit | NA | f() | 3 | , | NA | John Mount et al | https://cran.r-project.org/package=wrapr |

lambda | f | f(.(x) + 1) | f(.(x) + .(y)) | No | Implicit. Inferred from expression | .(x) | f() | 3 | NA | NA | Jim Hester | https://github.com/jimhester/lambda |

lambdass | ~~ | ~~ ..1 + 1 | ~~ ..1 + ..2 | No | Implicit. Only ..N arg syntax allowed | ..1 | ~~ | 2 | NA | NA | TobCap | https://github.com/TobCap/lambdass |

lambdass | f.() | f.(x, x + 1) | f.(x, y, x + y) | Yes | Explicit | NA | f() | 3 | , | NA | NA | NA |

lambdass | %->% | f(x) %->% {x + 1} | f(x, y) %->% {x + y} | Yes | Explicit | NA | f()%->%{} | 9 | `%->% | NA | NA | NA |

functional | “->” | NA | x ~ y -> x + y | Yes | Explicit | NA | NA | NA | NA | messes with <- operator! | Konrad Rudolph | https://github.com/klmr/functional |

Not packaged | lambda | lambda(x ~ x + 1L) | lambda(x + y ~ x + y) | Yes | Explicit. Inferred from LHS of formula | NA | f(~) | 4 | ~ | NA | Edward Visel | https://alistaire.rbind.io/blog/anonymous-functions/ |

Not packaged | lambda | lambda(x:x + 1) | lambda(x, y:x + y) | Yes | Explicit | NA | f(:) | 4 | , | Code no longer available | Koji MAKIYAMA | https://rpubs.com/hoxo_m/lambdaR |

Not packaged | [] -> | [x] -> x + 1 | [x, y] -> x + y | Yes | Explicit | NA | []-> | 4 | `-> | Speculative Future R syntax | Lionel Henry | https://lionel-.github.io/2016/02/15/ideas-for-an-updated-r-syntax/ |

## Features of an anonymous function syntax

I’ve tried to come up with a taxonomy to help classify the various syntaxes. I can’t say I was hugely successful, but it helped me organise my thoughts a little.

**Explicit Arguments**: The syntax has a list of formal arguments provided separate to the body of the function- Arguments provided as a separate list
- e.g.
`Base R`

- e.g.
- Arguments are parsed out of a separate expression (which is not the expression which becomes the body of the anonymous function)
- e.g.
`gsubfn`

where names are parsed out of the expression on the LHS of the formula

- e.g.

- Arguments provided as a separate list
**Implicit Arguments**: The syntax only provides the body of the anonymous function, and the formal arguments are never separately listed- Arguments are pre-defined
- e.g.
`rlang::as_function`

pre-defines`.x`

and`.y`

as the formal args. - The
`lambdass`

package uses the the`..1`

,`..2`

argument form in one of its implementations.

- e.g.
- Arguments are parsed from the expression which becomes the function body
- .e.g the one-sided formula from
`gsubfn`

- .e.g the one-sided formula from

- Arguments are pre-defined
**Implicit Arguments with discrimination**: arguments are parsed from the given expression, and there is some way to indicate whether this is a variable from the enclosing environment or is a formal argument.- Only Jim Hester’s
`lambda`

does this. Variables specified using the`bquote()`

syntax of`.(x)`

are considered formal arguments, everything else is a variable from the enclosing environment.

- Only Jim Hester’s
**Function Creation****Function Call**: Use a short function call to hide the explicit call to`function()`

.- This is the most common route of creation e.g.
`rlang::as_function()`

,`pryr::f()`

- This is the most common route of creation e.g.
**Exsiting Operator**: Existing operator is repurposed to create function- e.g.
`lambdass`

hijacks the operation of`~`

- e.g. Lionel Henry’s syntax idea hijacks the
`->`

operator - e.g. Konrad Rudolph’s
`functional`

package/module also hijacks the`->`

operator and thus is only useable in code which uses`=`

for assignment.

- e.g.
**New operator**: New operator used to create function- Only
`lambdass`

does this with the`%->%`

infix operator

- Only

## Notes on current implementations

Some of my thoughts/notes on the current implementations

**Base R**- Some would consider having to write out
`function() {}`

makes this a*verbose*syntax. Others would say I’m wrong.

- The
`function()`

text and the separate explicit list of formal arguments seems mostly redundant for a large percentage of cases where anonymous functions must be used i.e. single formal argument and small function body.

- Some would consider having to write out
`rlang::as_function()`

- Very handy within the
`purrr`

family of functions. **BUT**: No ability to customise argument names!

- Very handy within the
`pryr::f()`

- Supports both implicit and explicit formal arguments
- Doesn’t use the leading
`~`

like`purrr/rlang`

.

`nofrills::fn()`

- Only explicit formal arguments
- Uses
`~`

- Full support for
`tidyverse`

style Non-standard evaluation! This seems like overkill for my use case.

`gsubfn::as.function.formula()`

- Supports both implicit and explicit formal arguments
- Uses a leading
`~`

like`purrr/rlang`

. - When using explicit formal args, the LHS (confusingly) looks like an expression itself!

`wrapr::lambda()`

- Explicit formal arguments only
- Doesn’t use leading
`~`

**Jim Hester’s lambda**- I like that it allows you to explicitly indicate variables in the expression body
that should be formal arguments using
`bquote()`

substitution syntax. I just find the`bquote()`

substitution syntax too fiddly/verbose!

- I like that it allows you to explicitly indicate variables in the expression body
that should be formal arguments using
`lambdass::f.()`

- Explicit formal arguments only
- The other two forms for creating anonymous functions are a bit too funky for my liking!
- Overriding the operation of
`~`

seems fraught with issues - the
`%->%`

operator seems too fiddly/verbose

- Overriding the operation of

`functional`

module- The use of the forward assignment operator looks nice and mimics how other languages might do an anonymous function.
- However, because it uses the
`->`

, it actually messes with`<-`

as well, so you have to use`=`

to do actual assignment in your code if you use this.

## Thoughts on unifying implementation

After having looked at the existing implementations, here is my short list of what I think is needed for a good anonymous function implementation in R

- Use a function call to create the anonymous function - don’t use operator overloading or create a new operator.
- Allow both explicit and implicit formal arguments to give brevity when formal arguments are obvious, but flexibilty to explicitly define things
- Compatibility with
`rlang`

syntax. Ultimately,`purrr::map`

is where I want to use anonymous functions the most, so it’d be nice if this could be a drop-in replacement.

## Next steps

Future post: What might a unifying implementation look like?