R: Establish a restart point on the stack

with_restarts {rlang} | R Documentation

## 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 support tidy dots features.

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

### Life cycle

All the restart functions are in the questioning stage. It is not clear yet whether we want to recommend restarts as a style of programming in R.

### See Also

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

### Examples

    # 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(current_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 a calling handler that jumps to the restart. It has to
    # be calling 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 <- calling(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"))

[Package rlang version 0.4.0 Index]