Learn R Programming

gestalt (version 0.2.0)

context: Run an Action in an Ordered Context

Description

Programming in R typically involves:

  1. Making a context: assigning values to names.

  2. 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 env where z is 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 env where x is 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(), like let(), binds y and z lazily and comprehends quasiquotation.

Usage

let(`_data` = parent.frame(), ...)

run(`_data` = parent.frame(), `_expr`, ...)

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`.

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 envir argument of eval()).

...

Named expressions. An expression looks up values to the left of it, and takes precedence over those in `_data`. Quasiquotation of names and expressions is supported (see ‘Examples’).

`_expr`

Expression to evaluate (“run”). Quasiquotation is supported.

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

Run this code
# 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

Run the code above in your browser using DataLab