Home > Uncategorized > Performing a non-local return in R

Performing a non-local return in R

In most languages return is a statement, but in R it is a function (in fact R does not really have statements, it only has expressions). This function-like behavior of return is useful for figuring out the order in which operations are performed, e.g., the value returned by return(1)+return(2) tells us that binary operators are evaluated left to right.

R also supports lazy evaluation, operands are only evaluated when their value is required. The question of when a value might be required is a very complicated rabbit hole. In R’s case arguments to function calls are lazy and in the following code:

ret_a_b=function(a, b)
{
if (runif(1, -1, 1) < 0)
   a
else
   b
}
 
helpless=function()
{
ret_a_b(return(3), return(4))
 
return(99)
}

a call to helpless results in either 3 or 4 being returned.

This ability to perform non-local returns is just what is needed to implement exception handling recovery, i.e., jumping out of some nested function to a call potentially much higher up in the call tree, without passing back up through the intervening function called, when something goes wrong.

Having to pass the return-blob to every function called would be a pain, using a global variable would make life much simpler and less error prone. Simply assigning to a global variable will not work because the value being assigned is evaluated (it does not have to be, but R chooses to not to be lazy here). My first attempt to get what I needed into a global variable involved delayedAssign, but that did not work out. The second attempt made use of the environment created by a nested function definition, as follows:

# Create an environment containing a return that can be evaluated later. 
set_up=function(the_ret)
{
ret_holder=function()
   {
   the_ret
   }
 
return(ret_holder)
}
 
# For simplicity this is not nested in some complicated way
do_stuff=function()
{
# if (something_gone_wrong)
     get_out_of_jail()
 
return("done")
}
 
get_out_of_jail=0  # Out friendly global variable
 
control_func=function(a)
{
# Set up what will get called
get_out_of_jail <<- set_up(return(a))
 
# do some work
do_stuff()
return(0)
}
 
control_func(11)

and has the desired effect 🙂

  1. Kent Johnson
    February 24, 2014 13:07 | #1

    ISTM this is what exceptions and tryCatch are for, is there a reason you don’t use them? You can even make custom exceptions (signals) and catch your own signals while propagating unexpected ones. Hadley Wickham has a good writeup here:
    http://adv-r.had.co.nz/Exceptions-Debugging.html#condition-handling

  2. February 24, 2014 13:25 | #2

    @Kent Johnson
    try/catch is probably the most sensible construct to use for exception handling. The usage described above allows for dynamic selection of return point and if used will probably result in extremely hard to understand code. I would file this under there-be-dragons or Sunday afternoon doodling.

  3. February 24, 2014 23:54 | #3

    Have you seen callCC ?

  4. February 25, 2014 01:57 | #4

    @Sapsi
    callCC is another function in base that I was not aware of.

    A brief search for examples shows rather localized usage, e.g., returning early from an apply-like call (the sort of thing try/catch does not really handle well). The scoping of the function name complicates a more global usage; pointers to how it might be done simply welcome (but then this is what try/catch is for).

  1. No trackbacks yet.