Catch¶
Status: Stable
documented, exercised by the test suite and/or worked examples, with no known limitations recorded.
Description¶
Catch[expr]
Returns the argument of the first Throw generated while evaluating expr, or expr if none. Catch[expr, form] catches only a Throw[value, tag] whose tag matches form (tag is re-evaluated per comparison); Catch[expr, form, f] returns f[value, tag].
Examples (11)¶
Every input below was run against the current Mathilda build and its output recorded.
Basic examples (5)¶
In[1]:= Catch[a; b; Throw[c]; d; e]
Out[1]= c
In[2]:= f[x_] := If[x > 10, Throw[overflow], x!]; Catch[f[2] + f[11]]
Out[2]= overflow
In[3]:= Catch[Do[If[i! > 10^10, Throw[i]], {i, 100}]]
Out[3]= 14
In[4]:= Catch[Throw[a, u], u]
Out[4]= a
In[5]:= Catch[Throw[v, tg], tg, {#1, #2} &]
Out[5]= {v, tg}
Applications (6)¶
The first i whose square exceeds 20
Evaluation stops at the Throw, so d is never reached
A tagged throw caught by a matching form
The three-argument form returns f[value, tag]
A non-matching tag is left uncaught
With no throw, Catch is just the value of its body
Implementation notes¶
Algorithm. Catch is HoldFirst, Protected, so it drives evaluation of its
own body and can intercept a throw. builtin_catch calls evaluate on the held
first argument; if the result is not an in-flight Throw sentinel
(eval_is_inflight_throw) it is returned verbatim, so Catch[expr] with no throw
yields expr's value. When a sentinel comes back, the 1-argument form returns a
copy of the thrown value. The 2- and 3-argument forms are eligible only for a
tagged throw: the tag is re-evaluated (Wolfram semantics) and matched against
form through the pattern matcher (match); a non-match re-returns the same
sentinel so it propagates to an outer Catch, and a tagless Throw[value] is
never caught here. The 3-argument form returns the evaluated f[value, tag].
Data structures. The in-flight marker is an ordinary Throw[...] node
(arity 1–3), carried up the evaluator's normal return path rather than by
setjmp/longjmp — so every intervening frame runs its own cleanup and the
construct is leak-free (proven byte-identical to a no-throw control loop under
valgrind). env_new/match/env_free back the tag comparison.
Complexity / limits. The first Throw evaluated wins. evaluate_step's
argument loop short-circuits on a sentinel, so a throw deep inside Plus,
Times, Map, Sum, Table or a function application still reaches the nearest
Catch; a few consuming sites (Which/Switch, Scan, SelectFirst, the
iter_run family) carry an explicit propagation check the arg loop cannot
backstop. An uncaught throw is handled by eval_report_uncaught_throw.
ThrowisProtected;CatchisHoldFirst, Protected(it drives evaluation of its body itself, so it can intercept a throw;formandfevaluate normally).- Implemented by sentinel propagation through the evaluator's normal return paths (no
setjmp/longjmp), so every frame runs its own cleanup — leak-free. - The first
Throwevaluated wins; a taglessThrow[value]is not caught by a form-Catch. - An uncaught
Throw[value]/Throw[value, tag]returnsHold[Throw[...]]with aThrow::nocatchmessage; an uncaughtThrow[value, tag, f]returnsf[value, tag].
Attributes: HoldFirst, Protected.
References¶
See also: Throw, Return, Plus, Times, Map, Sum, Table
- Source:
src/funcprog.c - Specification:
docs/spec/builtins/control-flow.md - Tests:
tests/test_catch_throw.c - Tests:
tests/test_core_algebra.c - Tests:
tests/test_scan.c - Tests:
tests/test_sow_reap.c
Notes & additional examples¶
Notes¶
Catch[expr] catches the first Throw generated anywhere while expr is
evaluated and returns its value, or returns expr's own value if nothing is
thrown. Because the throw propagates through any enclosing head — Plus,
Map, Table, a function call — Catch is the tool for a non-local exit, where
Return only escapes a scope boundary.
Catch[expr, form] catches only a Throw[value, tag] whose tag matches form
(the tag is re-evaluated before each comparison); a throw with a non-matching tag,
and any tagless Throw[value], propagates to an outer Catch. Catch[expr, form,
f] returns f[value, tag]. The construct uses sentinel propagation rather than
setjmp/longjmp, so it is leak-free.