Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

return function inside with() block

Tags:

r

How does return() in a with() block work? Here is a test function

test_func <- function(df, y) {
  with(df,
       if(x > 10){
         message('Inside step 1')
         return(1)
       }
  )
  message("After step 1")

  if(y > 10){
    message('In side step 2')
    return(2)
  }  
  message("After step 2")
}
  • The function keeps going after return(1).
df <- data.frame(x = 11)
y <- 11
test_func(df, y)  ## result is 2

Output

Inside step 1
After step 1
In side step 2
[1] 2
  • return(1) doesn't return to the test_func() rather than get out the with() block
df <- data.frame(x = 11)
y <- 5
test_func(df, y) ## no result

Output

Inside step 1
After step 1
After step 2
like image 257
Chuliang Xiao Avatar asked Oct 16 '22 05:10

Chuliang Xiao


1 Answers

I think the main point here is that return() is designed to exit the current scope to the parent scope with a particular value. In the case of running

return("hello")
# Error: no function to return from, jumping to top level

You get an error because we are calling this from the global environment and there is no parent scope you are jumping back to. Note that thanks to R's lazy evaluation, parameters passed to a function are usually evaluated in the environment where they were passed from. So in this example

f <- function(x) x
f(return("hello"))
# Error in f(return("hello")) : 
#   no function to return from, jumping to top level

So because we are actually passing in the return() call to the function from the global environment, that's where the return will be evaluated and will return the same error. But note that this is not what with does, instead it captures the commands you pass and re-runs them in a new environment. It's closer to something like this

f <- function(x) eval(substitute(x))
f(return("hello"))
# [1] "hello"

This eval() creates a new level of scope we can escape from, and because we aren't evaluating the parameter passed to the function directly, we are just running those commands in a different environment, the return is no longer evaluated in the global environment so there's no error. So the function we created is basically the same as calling

with(NULL, return("hello"))
# [1] "hello"

which is different from something like

print(return("hello"))
# no function to return from, jumping to top level

where the parameter is directly evaluated. So the different meanings of return() really are side effects of non-standard evaluation in this case.

When return() is used inside a with(), you are not returning from the function that you called with() from, but you are returning from the scope that with() created for you to run your command.

How to fix this particular problem is already addressed by the comments left by @IceCreamToucan. You just need to move the return outside of the with().

like image 144
MrFlick Avatar answered Oct 19 '22 00:10

MrFlick