18
$\begingroup$

Version 12.2 brought us Confirm and Enclose. What's the point? Is it just that it's a slightly more convenient syntax than Throw and Catch with a conditional?

I can see where Confirm beats Assert: you don't need to On[Assert] first, and also Confirm is catchable.

$\endgroup$
8
  • 6
    $\begingroup$ It really deserves a "Background & Context" section in the docs. One difference is that Confirm throws a failure to an enclosing Enclose (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 common Catch/Throw failure conditions. $\endgroup$ – Michael E2 Feb 2 at 17:23
  • 6
    $\begingroup$ One of the major differences is that Enclose @ expr scopes lexically for inner Confirms (making these functions very predictable), while Catch @ expr scopes dynamically for inner Throws. The dynamical scoping of single argument Throw/Catch is 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, ConfirmBy and ConfrimMatch etc. are just really nice syntactically. $\endgroup$ – Sjoerd Smit Feb 2 at 17:48
  • 6
    $\begingroup$ @SjoerdSmit I think that exceptions inherently scope dynamically to be useful, they are all about bubbling up through call stack until the proper handler is reached. If they would scope lexically, then this would just be a somewhat less local form of Return, similar in spirit to two-argument Return. What I have been criticizing are Catch and Throw without 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. throwing Exception in Java, rather than its more specific subclass. $\endgroup$ – Leonid Shifrin Feb 2 at 19:45
  • 4
    $\begingroup$ @SjoerdSmit Yeah, I get your point. I just wanted to point out that dynamic scoping is inherent in exceptions, so if we are willing to use them, we should accept that. Of course I agree that in all cases where the lexical scope can be used, it is safer. What makes single-arg Catch and Throw dangerous 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. $\endgroup$ – Leonid Shifrin Feb 2 at 20:38
  • 4
    $\begingroup$ I propose @SjoerdSmit write up a summary of the commentary as a response. Or anyone else who might feel so inclined. $\endgroup$ – Daniel Lichtblau Feb 2 at 22:13
14
$\begingroup$

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).
$\endgroup$
2
  • 2
    $\begingroup$ Thank you very much for the complete overview. It is very helpful. $\endgroup$ – Michael E2 Feb 6 at 19:49
  • $\begingroup$ +1, very nice answer. I am happy I have not tried to answer myself, it would've likely been a half-hearted and / or needlessly bloated attempt compared to this one. $\endgroup$ – Leonid Shifrin Feb 6 at 22:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.