When must I use the Return function?
Return
is surprisingly under-documented, but I would still recommend reading Return
as a start. Return
has an undocumented feature which you can read about here
But none of the above really answers the question. Frankly, I can't come up with a situation that absolutely requires the use of Return
, although I can think of several where using Return
is convenient.
That said, I will try to give a rule-of-thumb. Use Return
to terminate the evaluation of a compound expression inside a function at any point before the final argument of the compound expression is evaluated. Does that read as gibberish? To make sense of it, recall that a; b; c
is actually the short form of CompoundExpression[a, b, c]
, which returns whatever c
evaluates to. To get a; b; c
to stop after evaluating b
and return, write a; Return[b]; c
instead.
Of course, in normal usage Return
will be placed in some kind of conditional construct. For example,
f[x_] := (a; If[x > 0, Return[a]]; b)
{f[42], f[-42]}
{a, b}
But this can be rewritten, as all the cases I can think of, to eliminate the use of Return
.
g[x_] := If[x > 0, a, b]
So when is it practical to use Return
in "real" code? I find myself using Return
in the definitions of functions that have non-standard evaluation; e.g., functions that have the the HoldAll
attribute. In such functions, there is a need to validate arguments that have been passed in. It is convenient to do this up front, using Return
to cut short evaluation should a bad argument be found. Here is a rather contrived example.
Clear @ h
SetAttributes[h, HoldAll];
h::badarg = "`1` = `2` is not valid";
h[x_, y_] :=
Module[{result = $default},
If[! MatchQ[x, _Integer /; x > 0], Message[h::badarg, "x", x]; Return[$Failed]];
If[! MatchQ[y, _Real], Message[h::badarg, "y", y ]; Return[$Failed]];
(* do some long computation here *)
result]
h[42, 42.]
$default
h[-42, 42.]
h::badarg: x = -42 is not valid
$Failed
h[42, 42]
h::badarg: y = 42 is not valid
$Failed
Another thing good to know about Return
is that it is not a function in the ordinary Mathematica sense. It's a symbol which has special meaning to the Mathematica evaluator and which causes a side effect. If Return
were an ordinary function a; Return[b]; c
would be the same as a; b; c
.
The non-functional behavior of Return
is demonstrated by the following rather peculiar sequence of evaluations.
expr = CompoundExpression[a, Return[b], c];
Return[b]
OwnValues @ expr
{HoldPattern[expr] :> Return[b]}
expr
b
I think the most direct answer to this question is: rarely.
Incorrect Usage
A great majority of the appearances of Return
that I see are either entirely superfluous or an indication of code in need of refactoring. For example I frequently see something like:
fn[x_] :=
Module[{vec},
vec = {1, 2, 3};
Do[vec[[i]] *= x, {i, 1, Length[vec]}];
Return[vec]
]
Return
is superfluous here as omitting it will produce the same result. And of course the rest this code would be written far more simply which is often true in such cases.
Correct Usage
Return
does have its place, as does its sister function Break
. These functions are the most direct way to exit an ongoing incremental evaluation, and optionally return a value. It is important to understand how they work but there are already questions on that subject:
- How does Return work?
- What can I use as the second argument to Return in my own functions?
Although mostly undocumented I often, even typically, use these functions with additional parameters:
- Is there a Break[] equivalent for short-circuiting in Table?
This not only extends the application of Return
and Break
but it more clearly defines the behavior of the operation which may make it more robust through version changes.
One must understand the order of evaluation and operation of Mathematica functions to effectively use Return
and Break
. For example it is not possible to Break[]
out of a Map
operation because Map
first applies the function, then evaluates the expression, as explained here:
- Scan vs. Map vs. Apply
However Scan
does use an incremental evaluation which means that we can Return
or Break
from it:
Scan[
If[# > 2, Return[#]] &,
{1, 2, 3, 4, 5}
]
3
Critically, though not illustrated, the Function
is never applied to elements 4
and 5
.
The question title is not when should I use Return
but rather when must I use return and that is even more rare, if ever. The operation above can also be written using Throw
and Catch
:
Scan[
If[# > 2, Throw[#]] &,
{1, 2, 3, 4, 5}
] // Catch
3
(Like Return
using the second parameter of Throw
and Catch
will make this operation more robust.)
Exclusive Usage
I am not prepared to say that there is ever a case where one must use Return
as there are so often clever alternative methods in Mathematica. However one example where it is at least far easier to use Return
is if it is not practical to insert a Catch
at an arbitrary place in the evaluation.
Suppose we have a function with multiple internal operations which itself accepts a function:
func[f_, dat_] := Array[f, #, #2] & ~MapIndexed~ dat ~Flatten~ {2}
func[f, {4, 1, 3}]
{{f[1], f[2], f[3]}, {f[2], f[4]}, {f[3], f[5]}, {f[4]}}
Without modifying func
we can use Return
to affect the evaluation in different ways:
func[If[# > 4, Return[{0, 7, 9}, Array], #] &, {4, 1, 3}]
func[If[# > 4, Return[{{1, 2}, {3}}, MapIndexed], #] &, {4, 1, 3}]
func[If[# > 4, Return["foo", func], #] &, {4, 1, 3}]
{{1, 2, 0}, {2, 7}, {3, 9}, {4}} {{1, 3}, {2}} "foo"
I apologize for obfuscation from what was perhaps a poorly chosen example function, but the point is that we can exit from and return a value for an individual use of Array
, or the one call to MapIndexed
, or the entire function func
.
Note that there was no example of Flatten
for the parameter of Return
because it does not work:
func[If[# > 4, Return["x", Flatten], #] &, {4, 1, 3}]
Return::nofunc: Function Flatten not found enclosing Return[x,Flatten]. >>
Hold[Return["x", Flatten]]
The reason for this is complicated and it is the subject of Rojo's answer found in the second bulleted link from the top of this post.
I think I find a situation that almost absolutely requires the use of Return
. Using Return
seems to be the only documented way to exit from a Dialog
and return a value:
Sadly(?) there's a undocumented function ExitDialog
that can be used for the task, too. (Still, Return
and ExitDialog
have subtle difference, read this post for more information.)