4

I did a test with nested return function in R, but without success. I came from Mathematica, where this code works well. Here is a toy code:

fstop <- function(x){
  if(x>0) return(return("Positive Number"))
}

f <- function(x){
  fstop(x)
  "Negative or Zero Number"
}

If I evaluate f(1), I get:

[1] "Negative or Zero Number"

When I expected just:

[1] "Positive Number"

The question is: there is some non-standard evaluation I can do in fstop, so I can have just fstop result, without change f function?

PS: I know I can put the if direct inside f, but in my real case the structure is not so simple, and this structure would make my code simpler.

Community
  • 1
  • 1
Murta
  • 2,037
  • 3
  • 25
  • 33
  • I'm not sure why you need this. There might be better alternatives we could suggest, but you need to provide more details regarding acceptable behavior. Maybe `stop` (which returns an error) would suit your needs? – Roland Mar 03 '16 at 12:05
  • @Roland in the real code, instead of return `print("Positive Number")`, I have to return distinct sorts of complex structures. Code would be more elegant if I could do that. – Murta Mar 03 '16 at 12:10
  • You should really show us the full original code, which might render the answer I gave you not relevant. – Tim Biegeleisen Mar 03 '16 at 12:10
  • I don't think you can force a return of the enclosing function. That's something very exotic. I'm sure there is better way (maybe as proposed by Tim, but your actual problem is still poorly defined). – Roland Mar 03 '16 at 12:12
  • Try creating an example without `print` if it is not what you will be using. – Pierre L Mar 03 '16 at 12:18
  • @Roland, my actual problem goes into database, make a select, put into an object, and if this object has some special conditions, I don't need to keep the evaluation, I need to return a special case. I do it in many parts of my code, and I would like to reduce it, from 5 lines to one. – Murta Mar 03 '16 at 12:18
  • @PierreLafortune yes, the real code return a value. I removed print (yeah, make the toy more compact), but believe it makes no diference. – Murta Mar 03 '16 at 12:30
  • Wasn't there a recent blog post (from Hadley maybe?) about making sure all your functions returned objects of the same "shape" and "structure" so that error conditions caused by "special cases" didn't need very special handling... You'd just have your DB access always return a list with an error code and a possible response... – Spacedman Mar 03 '16 at 14:22
  • 2
    @TimBiegeleisen specify what you mean by "R is not a functional language". – Pierre L Mar 03 '16 at 15:06
  • [Are statistical programming languages like R/SAS considered functional or procedural](http://stackoverflow.com/q/31145524) – Bhargav Rao Mar 03 '16 at 15:12

2 Answers2

7

Going to stick my neck out and say...

No.

Making a function return not to its caller but to its caller's caller would involve changing its execution context. This is how things like return and other control-flow things are implemented in the source. See:

https://github.com/wch/r-source/blob/trunk/src/main/context.c

Now, I don't think R level code has access to execution contexts like this. Maybe you could write some C level code that could do it, but its not clear. You could always write a do_return_return function in the style of do_return in eval.c and build a custom version of R... Its not worth it.

So the answer is most likely "no".

Community
  • 1
  • 1
Spacedman
  • 92,590
  • 12
  • 140
  • 224
  • Brave answer. No, it's not my idea to recompile my own R version. Thanks for the clarification :) – Murta Mar 05 '16 at 23:45
3

I think Spacedman is right, but if you're willing to evaluate your expressions in a wrapper, then it is possible by leveraging the tryCatch mechanism to break out of the evaluation stack.

First, we need to define a special RETURN function:

RETURN <- function(x) {
  cond <- simpleCondition("")  # dummy message required
  class(cond) <- c("specialReturn", class(cond))
  attr(cond, "value") <- x
  signalCondition(cond)
}

Then we re-write your functions to use our new RETURN:

f <- function(x) {
  fstop(x)
  "Negative or Zero"
}
fstop <- function(x) if(x > 0) RETURN("Positive Number")  # Note `RETURN` not `return`

Finally, we need the wrapper function (wsr here stands for "with special return") to evaluate our expressions:

wsr <- function(x) {
  tryCatch(
    eval(substitute(x), envir=parent.frame()),
    specialReturn=function(e) attr(e, "value")
) }

Then:

wsr(f(-5))
# [1] "Negative or Zero"
wsr(f(5))
# [1] "Positive Number"

Obviously this is a little hacky, but in day to day use would be not much different than evaluating expressions in with or calling code with source. One shortcoming is this will always return to the level you call wsr from.

BrodieG
  • 51,669
  • 9
  • 93
  • 146