8

From previous answers, replies and discussions it is known, while not formally documented (even in the most recent Wolfram Language Documentation Center), that Return can be used in a two-argument form in order to exit properly from specific places of pure function bodies.

For example:

Block[{i = 0}, While[i<10, Return[{"Exit Block", i}]]; "done While"]

returns

Return[{Exit Block,0}]

while, with the two-argument form:

Block[{i = 0},
    While[i<10, Return[{"Exit Block", i}, Block]]; "done While"
]

the Block is immediately exited with the expected outcome:

{Exit Block,0}.

Analogously, trying Return[..., While]:

Block[{i = 0}, 
    While[i < 10, Return[{"Exit Block", i}, While]]; "done While"
]

works as expected, so that only the enclosing While is exited and the subsequent instruction is executed, returning:

done While

Some more tests, though, show that this concept not always works.

For example, "If" cannot be exited:

Block[{i = 0}, If[i < 10, Return[{"Exit Block", i}, If]]; "done If"]

Return::nofunc: Function If not found enclosing Return[{Exit Block,0},If].

Hold[Return[{Exit Block,0},If]]

Apparently (at least in MMA 9), the two-argument form of Return works with:

Module
Block
Do
While
For
CompoundExpression

and does not seem to work with:

If
With
DynamicModule

Does anyone have a list of valid and invalid possibilities for the second argument of Return? Is there a clear rationale distinguishing what works from what does not work?

Karsten7
  • 27,448
  • 5
  • 73
  • 134
user8074
  • 1,592
  • 8
  • 7
  • 2
    I would probably use Throw and Catch, with tags for clarity, instead. – Michael E2 Sep 04 '15 at 17:54
  • 1
    In the way this is asked, this is a duplicate of this one. I'd suggest that you move the part of your question to a self-answer, showing how to return from a pure function, and assuming that the second argument of Return is a known piece of information (possibly linking to the question I mentioned). – Leonid Shifrin Sep 04 '15 at 17:55
  • @MichaelE2 I'd argue that using Throw and Catch is a bad style in cases when the second argument of Return can be used, from the language viewpoint. It will likely also be slower, although I did not benchmark. – Leonid Shifrin Sep 04 '15 at 17:56
  • @MichaelE2 Also, the second argument of Return can be used quite elegantly in other cases (where of course Throw and Catch can also be used), like e.g. in this answer. – Leonid Shifrin Sep 04 '15 at 18:00
  • @LeonidShifrin, actually I am asking for a list of possibilities. I have not found it anywhere. In a reply you also mention ReplaceAll, but in general the information is scattered and incomplete. As a second point, In your reply to the potential duplicate, you try to give a rationale connected to the expression tree (as far as I understood), but why, e.g. With does not work and Block does? – user8074 Sep 04 '15 at 18:11
  • 5
    @user8074 The second answer is not by me, but by Rojo. In general, you won't find a specific list of heads, because the second argument of Return can also work with user-defined heads. So, the only way to understand this to to carefully read Rojo's answer, which contains full explanation. Re: With vs Block: the reason is that With replaces the constants in code before evaluating the body, and then evaluates the code, so by the time code executes, there is no With up in the stack. With Block, it is not so, because it has to undo its dynamic environment after its body executes. – Leonid Shifrin Sep 04 '15 at 18:15
  • I am afraid that soon I will have to vote to close this one as duplicate of this, if it isn't reformulated - in the current form it is an exact duplicate, and in the mean time it is gaining votes and soon some answers will appear, duplicating the effort and fragmenting our knowledge base in this direction. – Leonid Shifrin Sep 04 '15 at 22:12
  • @LeonidShifrin: Thanks for ref to question & Rojo's answer - had not seen it before, very nice... – ciao Sep 04 '15 at 22:21
  • @ciao Sure, glad if you find those useful. – Leonid Shifrin Sep 04 '15 at 23:07
  • 2
    @ciao Ok, I looked again closer at Rojo's answer, and my current conclusion is that while this question is an exact duplicate of the linked question, Rojo's answer doesn't fully answer it, since Rojo focuses on user-defined symbols, rather than common built-in ones. So, the answer listing all common constructs and explaining why Return works / doesn't work for them, one by one, will be useful and will complement Rojo's answer. This is an interesting situation - personally, I'd keep this question to attract attention, and then merge with the ealier one once some answers appear here. – Leonid Shifrin Sep 04 '15 at 23:23

1 Answers1

2

This handling of If is more or less correct behavior. The condition evaluates to True so If exits with its second argument as the result. That second arg is Return[{"Exit Block", i}, If]. At this point the evaluator figures out there is no surrounding If from which the Return can actually return, and it (apparently) bubbles up to the top level. At worst this might be a clumsy way of handling an error state.

To see it in slightly more detail, note that the code below does not result in a value being assigned to a.

In[11]:= a = 
 Block[{i = 0}, If[i < 10, Return[{"Exit Block", i}, If]]; "done If"]

During evaluation of In[11]:= Return::nofunc: Function If not found enclosing Return[{Exit Block,0},If]. >>

(* Out[11]= Hold[Return[{"Exit Block", 0}, If]] *)

In[12]:= a

(* Out[12]= a *)
Jason B.
  • 68,381
  • 3
  • 139
  • 286
Daniel Lichtblau
  • 58,970
  • 2
  • 101
  • 199