Function Repository Resource:

NestedCatch

Source Notebook

Propagate a Throw upward to the outermost NestedCatch

Contributed by: Richard Hennigan (Wolfram Research)

ResourceFunction["NestedCatch"][expr]

catches the first evaluation of Throw[e] and propagates it to the next enclosing NestedCatch if available.

ResourceFunction["NestedCatch"][expr,form]

returns Throw[value,tag] if there is an enclosing NestedCatch ready to catch form, and otherwise returns value from the first Throw[value,tag] for which form matches tag.

ResourceFunction["NestedCatch"][expr,form,f]

returns f[value,tag].

Details and Options

ResourceFunction["NestedCatch"][expr,] always returns the value of expr if no Throw was generated during the evaluation.
The value for form can be any expression and is often a pattern.
In Throw[value,tag], the tag is reevaluated every time it is compared to form.
A Throw that is caught by Catch will not be propagated to any enclosing ResourceFunction["NestedCatch"].

Examples

Basic Examples (3) 

Exit to the enclosing NestedCatch as soon as the Throw is evaluated:

In[1]:=
ResourceFunction["NestedCatch"][Echo[a]; Throw[Echo[b]]; Echo[c]]
Out[1]=

When nested, the Throw propagates upward to the outermost NestedCatch:

In[2]:=
ResourceFunction["NestedCatch"][
 Echo[a];
 ResourceFunction["NestedCatch"][Echo[b]; Throw[Echo[c]]; Echo[d]];
 Echo[e]
 ]
Out[2]=

Compare to Catch:

In[3]:=
Catch[
 Echo[a];
 Catch[Echo[b]; Throw[Echo[c]]; Echo[d]];
 Echo[e]
 ]
Out[3]=

Define a function that can "throw an exception", but will catch its own Throw if necessary:

In[4]:=
f[x_] := ResourceFunction["NestedCatch"][
   If[x > 10, Throw[overflow], x!]];
In[5]:=
f[10]
Out[5]=
In[6]:=
f[11]
Out[6]=

With no outer NestedCatch, the individual values are returned:

In[7]:=
Table[Echo[f[i]], {i, 3, 15, 3}]
Out[7]=

Wrap in an additional NestedCatch so that the Throw will propagate to the top level:

In[8]:=
ResourceFunction["NestedCatch"][Table[Echo[f[i]], {i, 3, 15, 3}]]
Out[8]=

The Throw only propagates upwards to the outermost NestedCatch; the usual Catch is unaffected:

In[9]:=
Catch[Table[Echo[f[i]], {i, 3, 15, 3}]]
Out[9]=
In[10]:=
g[x_] := If[x > 10, Throw[overflow], x!]
In[11]:=
ResourceFunction["NestedCatch"][
 Table[Catch[Echo[g[i]]], {i, 3, 15, 3}]]
Out[11]=

Scope (5) 

The outermost enclosing NestedCatch catches the Throw:

In[12]:=
ResourceFunction[
 "NestedCatch"][{ResourceFunction["NestedCatch"][{a, Throw[b], c}], d,
   e}]
Out[12]=

Compare to Catch, where the nearest enclosing Catch catches the Throw:

In[13]:=
Catch[{Catch[{a, Throw[b], c}], d, e}]
Out[13]=

NestedCatch picks up the first Throw that is evaluated:

In[14]:=
ResourceFunction["NestedCatch"][{Throw[a], Throw[b], Throw[c]}]
Out[14]=

A function that can throw a number of different exceptions:

In[15]:=
f[x_] := Which[
  x < 0, Throw[x, error[negative]],
  x == 0, Throw[x, error[zero]],
  True, 1/Sqrt[x]]

A handler for the possible exceptions:

In[16]:=
handler = Function[{value, tag}, tag /. {error[negative] :> Indeterminate, error[zero] :> Infinity, _ :> Throw[value, tag]}];
In[17]:=
ff[x_] := ResourceFunction["NestedCatch"][f[x], error[_], handler]
In[18]:=
ff /@ {-2, -1, 0, 1, 2, 3}
Out[18]=

Propagate a particular exception upwards with an outer NestedCatch:

In[19]:=
fff[xs_List] := ResourceFunction["NestedCatch"][ff /@ xs, error[zero], Print["zero is forbidden"] &]
In[20]:=
fff[{-2, -1, 0, 1, 2, 3}]
In[21]:=
fff[{-2, -1, 1, 2, 3}]
Out[21]=

The inner NestedCatch catches the Throw:

In[22]:=
ResourceFunction["NestedCatch"][
 b[ResourceFunction["NestedCatch"][Throw[a, u], u]], v]
Out[22]=

The outer NestedCatch catches the Throw:

In[23]:=
ResourceFunction["NestedCatch"][
 b[ResourceFunction["NestedCatch"][Throw[a, u], v]], u]
Out[23]=

As long as the tag matches the form being caught, the Throw will continue to propagate upwards:

In[24]:=
ResourceFunction["NestedCatch"][
 c[ResourceFunction["NestedCatch"][
   b[ResourceFunction["NestedCatch"][Throw[a, u], u]], _]], v]
Out[24]=

Applications (2) 

Write a set of functions that each handle errors at the top level:

In[25]:=
RealSquareRoot[x_] := With[{r = ResourceFunction["NestedCatch"][rsqrt[x]]}, r /; checkResult[r, RealSquareRoot]];
RealSquareRoots[list_List] := With[{r = ResourceFunction["NestedCatch"][RealSquareRoot /@ list]}, r /; checkResult[r, RealSquareRoots]];
In[26]:=
General::nnarg = "The argument `1` is not greater than or equal to zero.";
rsqrt[x_] := If[TrueQ[x >= 0], Sqrt[x], Throw[fail[x, "nnarg"]]];
checkResult[fail[x_, tag_], s_] := Message[MessageName[s, tag], x];
checkResult[_, _] := True;

Since the Throw propagates to the top, only one message is printed for each top-calling function:

In[27]:=
RealSquareRoot[5]
Out[27]=
In[28]:=
RealSquareRoot[-5]
Out[28]=
In[29]:=
RealSquareRoots[Range[5]]
Out[29]=
In[30]:=
RealSquareRoots[-Range[5]]
Out[30]=

Version History

  • 1.0.0 – 20 May 2020

Related Resources

License Information