Stable lifecycle

  • as_function() transforms a one-sided formula into a function. This powers the lambda syntax in packages like purrr.

  • as_closure() first passes its argument to as_function(). If the result is a primitive function, it regularises it to a proper closure (see is_function() about primitive functions). Some special control flow primitives like if, for, or break can't be coerced to a closure.

as_function(x, env = caller_env())

is_lambda(x)

as_closure(x, env = caller_env())

Arguments

x

A function or formula.

If a function, it is used as is.

If a formula, e.g. ~ .x + 2, it is converted to a function with up to two arguments: .x (single argument) or .x and .y (two arguments). The . placeholder can be used instead of .x. This allows you to create very compact anonymous functions (lambdas) with up to two inputs. Functions created from formulas have a special class. Use is_lambda() to test for it.

Lambdas currently do not support nse-force, due to the way the arguments are handled internally.

env

Environment in which to fetch the function in case x is a string.

Examples

f <- as_function(~ .x + 1)
f(10)
#> [1] 11

g <- as_function(~ -1 * .)
g(4)
#> [1] -4

h <- as_function(~ .x - .y)
h(6, 3)
#> [1] 3

# Functions created from a formula have a special class:
is_lambda(f)
#> [1] TRUE
is_lambda(as_function(function() "foo"))
#> [1] FALSE

# Primitive functions are regularised as closures
as_closure(list)
#> function (...) 
#> .Primitive("list")(...)
as_closure("list")
#> function (...) 
#> .Primitive("list")(...)

# Operators have `.x` and `.y` as arguments, just like lambda
# functions created with the formula syntax:
as_closure(`+`)
#> function (e1, e2, .x = e1, .y = e2) 
#> {
#>     if (missing(.x)) {
#>         if (missing(e1)) {
#>             abort("Must supply `e1` or `.x` to binary operator")
#>         }
#>         .x <- e1
#>     }
#>     else if (!missing(e1)) {
#>         abort("Can't supply both `e1` and `.x` to binary operator")
#>     }
#>     if (missing(.y) && !missing(e2)) {
#>         .y <- e2
#>     }
#>     else if (!missing(e2)) {
#>         abort("Can't supply both `e2` and `.y` to binary operator")
#>     }
#>     if (missing(.y)) 
#>         .x
#>     else .x + .y
#> }
#> <environment: 0x560eae141798>
as_closure(`~`)
#> function(.x, .y) {
#>       if (is_missing(substitute(.y))) {
#>         new_formula(NULL, substitute(.x), caller_env())
#>       } else {
#>         new_formula(substitute(.x), substitute(.y), caller_env())
#>       }
#>     }
#> <bytecode: 0x560ea8ee9b58>
#> <environment: 0x560eae24e320>

# Use a regular function for tidy evaluation, also when calling functions
# that use tidy evaluation:
## Bad:
e <- as_function(~ as_label(ensym(.x)))
## Good:
e <- as_function(function(x) as_label(ensym(x)))

e(y)
#> [1] "y"