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

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()
#> [1] "returned"
# 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()
#> [1] "returned"
# 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()
#> will this be called? #> will this be called?
# 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)
#> restarting...
#> [1] "return value"
# 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)
#> [1] "normal value"
# Default value for bad input: fn(FALSE)
#> Warning: Creating a condition with cnd_signal() is deprecated as of rlang 0.3.0. #> Please use signal() instead. #> This warning is displayed once per session.
#> [1] ""
# 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)
#> character(0)
# You can use restarting() to create restarting handlers easily: with_handlers(fn(FALSE), default_empty_string = restarting("rst_null"))
#> NULL