For better or worse, Mathematica provides a wealth of constructs that allow you to do non-local transfers of control, including Return
, Catch
/Throw
, Abort
and Goto
. However, these kinds of non-local transfers of control often conflict with writing robust programs that need to ensure that clean-up code (like closing streams) gets run. Many languages provide ways of ensuring that clean-up code gets run in a wide variety of circumstances; Java has its finally
blocks, C++ has destructors, Common Lisp has UNWIND-PROTECT
, and so on.
In Mathematica, I don't know how to accomplish the same thing. I have a partial solution that looks like this:
Attributes[CleanUp] = {HoldAll};
CleanUp[body_, form_] :=
Module[{return, aborted = False},
Catch[
CheckAbort[
return = body,
aborted = True];
form;
If[aborted,
Abort[],
return],
_, (form; Throw[##]) &]];
This certainly isn't going to win any beauty contests, but it also only handles Abort
and Throw
. In particular, it fails in the presence of Return
; I figure if you're using Goto
to do this kind of non-local control in Mathematica you deserve what you get.
I don't see a good way around this. There's no CheckReturn
for instance, and when you get right down to it, Return
has pretty murky semantics. Is there a trick I'm missing?
EDIT: The problem with Return
, and the vagueness in its definition, has to do with its interaction with conditionals (which somehow aren't "control structures" in Mathematica). An example, using my CleanUp
form:
CleanUp[
If[2 == 2,
If[3 == 3,
Return["foo"]]];
Print["bar"],
Print["cleanup"]]
This will return "foo" without printing "cleanup". Likewise,
CleanUp[
baz /.
{bar :> Return["wongle"],
baz :> Return["bongle"]},
Print["cleanup"]]
will return "bongle" without printing cleanup. I don't see a way around this without tedious, error-prone and maybe impossible code-walking or somehow locally redefining Return
using Block
, which is heinously hacky and doesn't actually seem to work (though experimenting with it is a great way to totally wedge a kernel!)