Learn R Programming

rlang (version 0.2.2)

stack: 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.

Life cycle

These functions are in the questioning stage. We are no longer convinced they belong in rlang as they are mostly for REPL interaction and runtime inspection rather than function development.

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 lazyeval 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().

[[2]: R:[2 [base::sys.nframe()]: R:base::sys.nframe()

Examples

Run this code
# NOT RUN {
# 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 lazyeval _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)
# }

Run the code above in your browser using DataLab