Learn R Programming

rlang (version 0.1)

with_restarts: Establish a restart point on the stack

Description

Restart points are named functions that are established with with_restarts(). Once established, you can interrupt the normal execution of R code, jump to the restart, and resume execution from there. Each restart is established along with a restart function that is executed after the jump and that provides a return value from the establishing point (i.e., a return value for with_restarts()).

Usage

with_restarts(.expr, ...)

Arguments

.expr

An expression to execute with new restarts established on the stack. This argument is passed by expression and supports unquoting. It is evaluated in a context where restarts are established.

...

Named restart functions. The name is taken as the restart name and the function is executed after the jump. These dots are evaluated with explicit splicing.

Details

Restarts are not the only way of jumping to a previous call frame (see return_from() or return_to()). However, they have the advantage of being callable by name once established.

See Also

return_from() and return_to() for a more flexible way of performing a non-local jump to an arbitrary call frame.

Examples

Run this code
# NOT RUN {
# Restarts are not the only way to jump to a previous frame, but
# they have the advantage of being callable by name:
fn <- function() with_restarts(g(), my_restart = function() "returned")
g <- function() h()
h <- function() { rst_jump("my_restart"); "not returned" }
fn()

# Whereas a non-local return requires to manually pass the calling
# frame to the return function:
fn <- function() g(get_env())
g <- function(env) h(env)
h <- function(env) { return_from(env, "returned"); "not returned" }
fn()


# rst_maybe_jump() checks that a restart exists before trying to jump:
fn <- function() {
  g()
  cat("will this be called?\n")
}
g <- function() {
  rst_maybe_jump("my_restart")
  cat("will this be called?\n")
}

# Here no restart are on the stack:
fn()

# If a restart point called `my_restart` was established on the
# stack before calling fn(), the control flow will jump there:
rst <- function() {
  cat("restarting...\n")
  "return value"
}
with_restarts(fn(), my_restart = rst)


# Restarts are particularly useful to provide alternative default
# values when the normal output cannot be computed:

fn <- function(valid_input) {
  if (valid_input) {
    return("normal value")
  }

  # We decide to return the empty string "" as default value. An
  # altenative strategy would be to signal an error. In any case,
  # we want to provide a way for the caller to get a different
  # output. For this purpose, we provide two restart functions that
  # returns alternative defaults:
  restarts <- list(
    rst_empty_chr = function() character(0),
    rst_null = function() NULL
  )

  with_restarts(splice(restarts), .expr = {

    # Signal a typed condition to let the caller know that we are
    # about to return an empty string as default value:
    cnd_signal("default_empty_string")

    # If no jump to with_restarts, return default value:
    ""
  })
}

# Normal value for valid input:
fn(TRUE)

# Default value for bad input:
fn(FALSE)

# Change the default value if you need an empty character vector by
# defining an inplace handler that jumps to the restart. It has to
# be inplace because exiting handlers jump to the place where they
# are established before being executed, and the restart is not
# defined anymore at that point:
rst_handler <- inplace(function(c) rst_jump("rst_empty_chr"))
with_handlers(fn(FALSE), default_empty_string = rst_handler)

# You can use restarting() to create restarting handlers easily:
with_handlers(fn(FALSE), default_empty_string = restarting("rst_null"))
# }

Run the code above in your browser using DataLab