R: Call stack information

stack {rlang} | R Documentation

## Call stack information

### Description

The `eval_` and `call_` families of functions provide a replacement for the base R functions prefixed with `sys.` (which are all about the context stack), as well as for `parent.frame()` (which is the only base R function for querying the call stack). The context stack includes all R-level evaluation contexts. It is linear in terms of execution history but due to lazy evaluation it is potentially nonlinear in terms of call history. The call stack history, on the other hand, is homogenous.

### Usage

    global_frame()

    current_frame()

    ctxt_frame(n = 1)

    call_frame(n = 1, clean = TRUE)

    ctxt_depth()

    call_depth()

    ctxt_stack(n = NULL, trim = 0)

    call_stack(n = NULL, clean = TRUE)

### Arguments

`n` | The number of frames to go back in the stack.
`clean` | Whether to post-process the call stack to clean non-standard frames. If `TRUE`, suboptimal call-stack entries by `base::eval()` will be cleaned up: the duplicate frame created by `eval()` is eliminated.
`trim` | The number of layers of intervening frames to trim off the stack. See `stack_trim()` and examples.

### Details

`ctxt_frame()` and `call_frame()` return a `frame` object containing the following fields: `expr` and `env` (call expression and evaluation environment), `pos` and `caller_pos` (position of current frame in the context stack and position of the caller), and `fun` (function of the current frame). `ctxt_stack()` and `call_stack()` return a list of all context or call frames on the stack. Finally, `ctxt_depth()` and `call_depth()` report the current context position or the number of calling frames on the stack.

The base R functions take two sorts of arguments to indicate which frame to query: `which` and `n`. The `n` argument is straightforward: it's the number of frames to go down the stack, with `n = 1` referring to the current context. The `which` argument is more complicated and changes meaning for values lower than 1. For the sake of consistency, the rlang functions all take the same kind of argument `n`. This argument has a single meaning (the number of frames to go down the stack) and cannot be lower than 1.

Note finally that `parent.frame(1)` corresponds to `call_frame(2)$env`, as `n = 1` always refers to the current frame. This makes the `_frame()` and `_stack()` functions consistent: `ctxt_frame(2)` is the same as `ctxt_stack()[[2]]`. Also, `ctxt_depth()` returns one more frame than `base::sys.nframe()` because it counts the global frame. That is consistent with the `_stack()` functions which return the global frame as well. This way, `call_stack(call_depth())` is the same as `global_frame()`.

### Life cycle

These functions are soft-deprecated and replaced by `trace_back()`.

### Examples

    # Expressions within arguments count as contexts
    identity(identity(ctxt_depth())) # returns 2

    # But they are not part of the call stack because arguments are
    # evaluated within the calling function (or the global environment
    # if called at top level)
    identity(identity(call_depth())) # returns 0

    # The context stacks includes all intervening execution frames. The
    # call stack doesn't:
    f <- function(x) identity(x)
    f(f(ctxt_stack()))
    f(f(call_stack()))

    g <- function(cmd) cmd()
    f(g(ctxt_stack))
    f(g(call_stack))

    # The rlang _stack() functions return a list of frame
    # objects. Use purrr::transpose() or index a field with
    # purrr::map()'s to extract a particular field from a stack:

    # stack <- f(f(call_stack()))
    # purrr::map(stack, "env")
    # purrr::transpose(stack)$expr

    # current_frame() is an alias for ctxt_frame(1)
    fn <- function() list(current = current_frame(), first = ctxt_frame(1))
    fn()

    # While current_frame() is the top of the stack, global_frame() is
    # the bottom:
    fn <- function() {
      n <- ctxt_depth()
      ctxt_frame(n)
    }
    identical(fn(), global_frame())

    # ctxt_stack() returns a stack with all intervening frames. You can
    # trim layers of intervening frames with the trim argument:
    identity(identity(ctxt_stack()))
    identity(identity(ctxt_stack(trim = 1)))

    # ctxt_stack() is called within fn() with intervening frames:
    fn <- function(trim) identity(identity(ctxt_stack(trim = trim)))
    fn(0)

    # We can trim the first layer of those:
    fn(1)

    # The outside intervening frames (at the fn() call site) are still
    # returned, but can be trimmed as well:
    identity(identity(fn(1)))
    identity(identity(fn(2)))

    g <- function(trim) identity(identity(fn(trim)))
    g(2)
    g(3)

[Package rlang version 0.3.2 Index]