Confirm and Enclose are higher-level constructs meant to provide a convenient (and readable) notation for recovering from expected but somewhat unusual conditions involving program data or state. They happen to be built upon the lower-level Throw/Catch mechanism, but part of the point of their introduction is to hide such implementation details from the user.
Throw is a very low-level abstraction, akin to a goto statement. In itself, it does not offer someone reading the code many clues as to its purpose. By contrast, the various Confirm functions are meant to indicate to the reader that there are recovery strategies in place for:
- unusual data (
Confirm, ConfirmBy and ConfirmMatch)
- unusual program state (
ConfirmAssert)
- unusual warning messages (
ConfirmQuiet)
The Confirm family join a growing list of non-local return features within Mathematica. In principle, all such constructs could be implemented using only Throw, Catch and CheckAll. Nevertheless, there are many features for specific purposes and with specific names. Consider:
| Signaller |
Handler |
Handler Scope |
Unwinding Continues |
Error If Unhandled |
Primary Use |
Throw |
Catch |
dynamic |
if tag unmatched |
yes |
general-purpose non-local exit facility |
Abort |
CheckAbort |
dynamic |
no |
yes |
error-handling |
Message |
Check |
dynamic |
if messages unmatched |
no |
warning-handling |
Assert |
$AssertFunction |
dynamic |
n/a |
no |
checking programming preconditions |
Confirm
ConfirmBy
ConfirmMatch |
Enclose |
lexical or dynamic |
if tag unmatched |
yes |
recovering from unusual data |
ConfirmAssert |
Enclose |
lexical or dynamic |
if tag unmatched |
yes |
recovering from unusual program state |
ConfirmQuiet |
Enclose |
lexical or dynamic |
if tag unmatched |
yes |
recovering from warnings |
| any |
WithCleanup |
dynamic |
yes |
depends upon signal |
resource disposal |
| any |
Internal` WithLocalSettings |
dynamic |
yes |
depends upon signal |
resource disposal |
| any |
CheckAll |
dynamic |
no |
depends upon signal |
backstopping exits |
Many of these names and functions have arisen due to history. Enclose and WithCleanup may represent a move by WRI to unify the handling patterns for expected but unusual program conditions. It might explain why there is no ConfirmNoAbort -- Abort can be reserved for truly unexpected program errors.
Some observations:
Throw/Catch are similar to the like-named symbols in Lisp, as well as longjmp/setjmp from C.
Abort/CheckAbort are similar to throw/try-catch in C++, Java and C#... except that with CheckAbort it can be difficult (or impossible) to identify the triggering signal using just $MessageList. So Throw/Catch are often used instead for error-handling.
ConfirmAssert and Assert are similar but perhaps the former should now be used for handling expected program conditions and the latter reserved for forensics during debugging sessions.
ConfirmQuiet is much like Check except that it integrates with Enclose.
CheckAll and Internal`WithLocalSettings are undocumented. But the recently introduced experimental symbol WithCleanup covers most of the functionality provided by the latter. WithCleanup is similar to unwind-protect in Lisp and try-finally in Java and C#. CheckAll is similar to a handler for type t in Lisp or try-catch(Throwable) in Java.
- As of V12.2, the lexical forms of
Confirm are simulated by dynamic Throw/Catch using private tags and some heuristic checks in Enclose. The abstraction is a bit leaky but likely not to the point of real harm... for fun try Enclose[Confirm[SymbolName@#]]&[Confirm] (working with held code is really hard in WL).
Confirmhas the attributeHoldAll, so you can construct more fine-grained responses to what should happen when e.g. messages are created. I think the "mountainElevation" example in the Applications section of theConfirmdocumentation could not have been done (readably) withThrowandCatch. Probably the idea was to create a more fine-tunable version ofCatchandThrow, but I don't really know, since I don't work for WRI ... – Rolf Mertig Feb 02 '21 at 17:04Confirmthrows a failure to an enclosingEnclose(bad name?) only if the expression evaluates to one of certain common forms of failure; otherwise, execution continues. The first example in "Applications" @Rolf points out shows how that might be syntactically convenient. It is perhaps no more than syntactic sugar for commonCatch/Throwfailure conditions. – Michael E2 Feb 02 '21 at 17:23Enclose @ exprscopes lexically for innerConfirms (making these functions very predictable), whileCatch @ exprscopes dynamically for innerThrows. The dynamical scoping of single argumentThrow/Catchis often very undesirable and Leonid takes every opportunity to point out that it's a defect of the language that they can completely break evaluation control because of that. In addition,ConfirmByandConfrimMatchetc. are just really nice syntactically. – Sjoerd Smit Feb 02 '21 at 17:48Return, similar in spirit to two-argumentReturn. What I have been criticizing areCatchandThrowwithout a tag, since then there is no way to be selective, and one can catch something that was intended to be caught higher up in the call stack. This would be like e.g. throwingExceptionin Java, rather than its more specific subclass. – Leonid Shifrin Feb 02 '21 at 19:45CatchandThroware bad because of the dynamic scoping, while for the lexically scopedEnclose/Confimit's not a problem to not have tags. And since most people are lazy and use single-argument forms,Enclose/Confirmis a useful addition. – Sjoerd Smit Feb 02 '21 at 19:55CatchandThrowdangerous compared to their 2 or 3 arg counterparts is IMO not so much the dynamic nature (which they all share), but the fact that they can't be nested / re-entrant, so to speak. Which of course matters for dynamic scope, because in general you can't control the full evaluation stack. – Leonid Shifrin Feb 02 '21 at 20:38Interpreterset included in Mathematica. The new built-ins offer a rich set of issues following the documentation pages. SinceCatch/Throware not interpreted it is hard to compare. It transfers the concepts of the flow management ofCatch/Throwto theInterpreter. It requires a proper concept of tagging and offers individualization by associations. It is possible to integrate theInterpreterinto usual built-ins as the main advantage. ... – Steffen Jaeschke Feb 05 '21 at 22:00