ISO defines exceptions to have the shape error(Formal, ImplDefined)
. Users can throw any term using throw/1, except for a variable.
In the good old days, before threads, SWI-Prolog implemented e.g., abort/0 by simply discarding the stacks and creating new ones. That is now dangerous as the running code may hold locks and discarding may require other cleanup. Cleanup is (I think) under discussion in ISO as SICStus call_cleanup/2 or SWI-Prolog’s derived setup_call_cleanup/3. That allows “getting out” while cleaning up using exceptions.
So, SWI-Prolog’s abort/0
is for a long time throw('$aborted')
. Of course, there is a problem as code like catch(goal, _, fail)
will now break our abort So, SWI-Prolog’s catch/3 recognises certain exceptions and when found, wraps the cleanup handler of catch/3 into call_cleanup(OrigHandler, throw(Ex))
, which will re-throw the exception. Of course, we can still block aborting for indefinite time by ensuring the OrigHandler never completes. I think that is bearable, at least it never caused big troubles.
I plan to extend this mechanism by re-implementing thread_exit(+Term)
using throw(exit(Term))
and allow halt/1 to be implemented as throw(halt(Status))
. The latter is also used by Python, which implements sys.exit(code)
as raise SystemExit(code)
. This is also the reason for considering implementing halt/1 this way as it allows clean unwinding of a mixed Python and Prolog stack for Janus.
So, I’m wondering whether other people are interested in this. It would be a very short pip, defining these reserved exception terms and agreeing on the call_cleanup/2 (or comparable mechanism) to make catch/3 bubble up these exceptions, even when caught. Roughly I see three options
- Give these exceptions nice standard names, e.g.,
abort
,halt(Code)
,exit(Term)
- Wrap them as e.g.
unwind(Term)
, for any exception of this shape bubbles up to the very top. - Give them $-reserved names. This is how it works for abort/0 now, but I’m a bit unhappy about that.