Last chance! 50% off unlimited learning
Sale ends in
fn()
enables you to create (anonymous) functions, of arbitrary call
signature. Use it in place of the usual function()
invocation whenever you
want to:
type less:
fn(x, y = 1 ~ x + y) function(x, y = 1) x + y
are equivalent
guard against changes in lexical scope: by enabling tidyverse
quasiquotation, fn()
allows you to
“burn in” values at the point of function creation (see
Pure functions via quasiquotation)
fn(..., ..env = parent.frame())
Function declaration, which supports quasiquotation.
Environment in which to create the function (i.e., the function<U+2019>s enclosing environment).
A function whose enclosing environment is ..env
.
A function declaration is an expression that specifies a function<U+2019>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.)
To the left of ~
, you write a conventional function-argument
declaration, just as in function(<arguments>)
: each of arg1
, arg2
,
…, argN
is either a bare argument (e.g., x
or ...
) or an
argument with default value (e.g., x = 1
).
To the right of ~
, you write the function body,
i.e., an expression of the arguments.
All parts of a function declaration support tidyverse quasiquotation:
To unquote values (of arguments or parts of the body), use !!
:
z <- 0 fn(x, y = !!z ~ x + y) fn(x ~ x > !!z)
To unquote argument names (with default value), use :=
(definition operator):
arg <- "y" fn(x, !!arg := 0 ~ x + !!as.name(arg))
To splice in a (formal) list of arguments, use !!!
:
fn(!!!alist(x, y = 0), ~ x + y)
(Note that the body, in this case, must be given as a one-sided formula.)
To write literal unquoting operators, use QUQ()
, QUQS()
:
library(dplyr)
my_summarise <- fn(df, ... ~ { group_by <- quos(...) df %>% group_by(QUQS(group_by)) %>% summarise(a = mean(a)) })
(Source: Programming with dplyr)
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<U+2019>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<U+2019>t unwittingly mutated an object that an earlier function depends upon.
Example <U+2014> 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 <U+2014> 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
# NOT RUN {
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
}
# }
Run the code above in your browser using DataLab