| context {gestalt} | R Documentation |
Run an Action in an Ordered Context
Description
Programming in R typically involves:
Making a context: assigning values to names.
Performing an action: evaluating an expression relative to a context.
let() and run() enable you to treat these procedures as reusable,
composable components.
-
let()makes a context: it lazily binds a sequence of ordered named expressions to a child of a given environment (by default, the current one).For instance, in an environment
envwherezis in scope,let(env, x = 1, y = x + 2, z = x * y * z)
is equivalent to calling
local({ x <- 1 y <- x + 2 z <- x * y * z environment() })except
let()binds the named expressions lazily (as promises) and comprehends tidyverse quasiquotation. -
run()performs an action: it evaluates an expression relative to an environment (by default, the current one) and, optionally, a sequence of lazily evaluated ordered named expressions.For instance, in an environment
envwherexis in scope,run(env, x + y + z, y = x + 2, z = x * y * z)
is equivalent to calling
local({ y <- x + 2 z <- x * y * z x + y + z })except
run(), likelet(), bindsyandzlazily and comprehends quasiquotation.
Usage
let(`_data` = parent.frame(), ...)
run(`_data` = parent.frame(), `_expr`, ...)
Arguments
_data |
Context of named values, namely an environment, list or data
frame; if a list or data frame, it is interpreted as an environment (like
the |
... |
Named expressions. An expression looks up values to the left of
it, and takes precedence over those in |
`_expr` |
Expression to evaluate (“run”). Quasiquotation is supported. |
Value
run() returns the evaluation of `_expr` in the combined
environment of `_data` and ....
let() returns an environment where the bindings in ... are in scope, as
promises, as if they were assigned from left to right in
a child of the environment defined by `_data`.
Composing Contexts
Contexts, as made by let(), have an advantage over ordinary local
assignments because contexts are both lazy and composable. Like
assignments, the order of named expressions in a context is significant.
For example, you can string together contexts to make larger ones:
foo <-
let(a = ., b = a + 2) %>>>%
let(c = a + b) %>>>%
run(a + b + c)
foo(1)
#> [1] 8
Earlier bindings can be overriden by later ones:
bar <-
foo[1:2] %>>>% # Collect the contexts of 'foo()'
let(c = c - 1) %>>>% # Override 'c'
run(a + b + c)
bar(1)
#> [1] 7
Bindings are promises; they are only evaluated on demand:
run(let(x = a_big_expense(), y = "avoid a big expense"), y) #> [1] "avoid a big expense"
Remark
“Contexts” as described here should not be confused with “contexts” in R's internal mechanism.
See Also
with() is like run(), but more limited because it doesn't
support quasiquotation or provide a means to override local bindings.
Examples
# Miles-per-gallon of big cars
mtcars$mpg[mtcars$cyl == 8 & mtcars$disp > 350]
run(mtcars, mpg[cyl == 8 & disp > 350])
run(mtcars, mpg[big_cars], big_cars = cyl == 8 & disp > 350)
# 'let()' makes a reusable local context for big cars
cars <- let(mtcars, big = cyl == 8 & disp > 350)
eval(quote(mpg[big]), cars) # Quoting restricts name lookup to 'cars'
run(cars, mpg[big]) # The same, but shorter and more transparent
run(cars, wt[big])
mtcars$wt[mtcars$cyl == 8 & mtcars$disp > 350]
# Precedence of names is from right to left ("bottom-up"):
a <- 1000
run(`_expr` = a + b, a = 1, b = a + 2) # 4: all references are local
run(list(a = 1), a + b, b = a + 2) # 4: 'b' references local 'a'
run(let(a = 1, b = a + 2), a + b) # 4: 'b' references local 'a'
run(let(a = 1, b = a + 2), a + b, a = 0) # 3: latter 'a' takes precedence
run(list(a = 1, b = a + 2), a + b) # 1003: 'b' references global 'a'
# Bound expressions are lazily evaluated: no error unless 'x' is referenced
run(`_expr` = "S'all good, man.", x = stop("!"))
run(let(x = stop("!")), "S'all good, man.")
let(x = stop("!")) # Environment binding 'x'
try(let(x = stop("!"))$x) # Error: !
# Quasiquotation is supported
a <- 1
run(let(a = 2), a + !!a) #> [1] 3
run(let(a = 1 + !!a, b = a), c(a, b)) #> [1] 2 2