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.
See vignette("stack")
for more information.
global_frame()
current_frame()
eval_frame(n = 1)
call_frame(n = 1, clean = TRUE)
eval_depth()
call_depth()
eval_stack(n = NULL, trim = 0)
call_stack(n = NULL, clean = TRUE)
stack_trim()
and examples.eval_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). eval_stack()
and call_stack()
return
a list of all context or call frames on the stack. Finally,
eval_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: eval_frame(2)
is the same as
eval_stack()[[2]]
. Also, eval_depth()
returns one
more frame than 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()
.
# Expressions within arguments count as contexts
identity(identity(eval_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(eval_stack()))
f(f(call_stack()))
g <- function(cmd) cmd()
f(g(eval_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:
if (requireNamespace("purrr", quietly = TRUE)) {
stack <- f(f(call_stack()))
purrr::map(stack, "env")
purrr::transpose(stack)$expr
}
# current_frame() is an alias for eval_frame(1)
fn <- function() list(current = current_frame(), first = eval_frame(1))
fn()
# While current_frame() is the top of the stack, global_frame() is
# the bottom:
fn <- function() {
n <- eval_depth()
eval_frame(n)
}
identical(fn(), global_frame())
# eval_stack() returns a stack with all intervening frames. You can
# trim layers of intervening frames with the trim argument:
identity(identity(eval_stack()))
identity(identity(eval_stack(trim = 1)))
# eval_stack() is called within fn() with intervening frames:
fn <- function(trim) identity(identity(eval_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