fn {nofrills}R Documentation

Low-cost anonymous functions

Description

fn() enables you to create (anonymous) functions, of arbitrary call signature. Use it in place of the usual ⁠function()⁠ invocation whenever you want to:

Usage

fn(..., ..env = parent.frame())

Arguments

...

Function declaration, which supports quasiquotation.

..env

Environment in which to create the function (i.e., the function’s enclosing environment).

Value

A function whose enclosing environment is ..env.

Function declarations

A function declaration is an expression that specifies a function’s arguments and body, as a comma-separated expression of the form

    arg1, arg2, ..., argN ~ body

or

    arg1, arg2, ..., argN, ~ body

(Note in the second form that the body is a one-sided formula. This distinction is relevant for argument splicing, see below.)

Quasiquotation

All parts of a function declaration support tidyverse quasiquotation:

Pure functions via quasiquotation

Functions in R are generally impure, i.e., the return value of a function will not in general be determined by the value of its inputs alone. This is because a function may depend on mutable objects in its lexical scope. Normally this isn’t an issue. But if you are working interactively and sourcing files into the global environment, say, or using a notebook interface (like Jupyter or R Notebook), it can be tricky to ensure that you haven’t unwittingly mutated an object that an earlier function depends upon.

Example — Consider the following function:

    a <- 1
    foo <- function(x) x + a

What is the value of foo(1)? It is not necessarily 2, because the value of a may have changed between the creation of foo() and the calling of foo(1):

    foo(1)  #> [1] 2
    a <- 0
    foo(1)  #> [1] 1

In other words, foo() is impure because the value of foo(x) depends not only on the value of x but also on the externally mutable value of a.

fn() enables you to write pure functions by using quasiquotation to eliminate such indeterminacy.

Example — With fn(), you can unquote a to “burn in” its value at the point of creation:

    a <- 1
    foo <- fn(x ~ x + !!a)

Now foo() is a pure function, unaffected by changes in its lexical scope:

    foo(1)  #> [1] 2
    a <- 0
    foo(1)  #> [1] 2

See Also

as_fn(), make_fn_aware(), curry_fn()

Examples

fn(x ~ x + 1)
fn(x, y ~ x + y)
fn(x, y = 2 ~ x + y)
fn(x, y = 1, ... ~ log(x + y, ...))

## to specify '...' in the middle, write '... = '
fn(x, ... = , y ~ log(x + y, ...))

## use one-sided formula for constant functions or commands
fn(~ NA)
fn(~ message("!"))

## unquoting is supported (using `!!` from rlang)
zero <- 0
fn(x = !!zero ~ x > !!zero)

## formals and function bodies can also be spliced in
f <- function(x, y) x + y
g <- function(y, x, ...) x - y
frankenstein <- fn(!!!formals(f), ~ !!body(g))
stopifnot(identical(frankenstein, function(x, y) x - y))

## mixing unquoting and literal unquoting is possible
if (suppressWarnings(require(dplyr))) {
  summariser <- quote(mean)

  my_summarise <- fn(df, ... ~ {
    group_by <- quos(...)
    df %>%
      group_by(QUQS(group_by)) %>%
      summarise(a = `!!`(summariser)(a))
  })

  my_summarise
}


[Package nofrills version 0.3.2 Index]