The built-in predicate on_exception/3
enables you to handle
exceptions to a specific goal:
on_exception(
?ExceptionCode,
:ProtectedGoal,
:Handler)
ProtectedGoal is executed. If all goes well, it will behave
just as if you had written ProtectedGoal without the
on_exception/3
wrapper. If an exception is raised while
ProtectedGoal is running, Prolog will abandon
ProtectedGoal entirely. Any bindings made by
ProtectedGoal will be undone, just as if it had failed. If the
exception occurred in the scope of a
call_cleanup(
Goal,
Cleanup)
, Cleanup will be
called.
Side-effects, such as asserts and retracts,
are not undone, just as they are not undone when a goal fails. After
undoing the bindings, Prolog tries to unify the exception term raised
with the ExceptionCode argument. If this unification succeeds,
Handler will be executed as if you had written
ExceptionCode=<the actual exception term>, Handler
If this unification fails, Prolog will keep searching up the ancestor list
looking for another exception handler. If it reaches Prolog's top-level (or a
break level) without having found a call to on_exception/3
with a matching
ExceptionCode, an appropriate error message is printed (using
print_message/2
).
ProtectedGoal need not be determinate. That is, backtracking into
ProtectedGoal is possible, and the exception handler becomes reactivated
in this case. However, if ProtectedGoal is determinate, the call
to on_exception/3
is also determinate.
The ProtectedGoal is logically inside the on_exception/3
form, but the Handler is not. If an exception is raised inside
the Handler, this on_exception/3
form will not be
reactivated. If you want an exception handler that protects itself,
you have to program it, perhaps like this:
recursive_on_exception_handler(Err, Goal, Handler) :- on_exception(Err, Goal, recursive_on_exception_handler(Err, Handler, Handler)).
Certain built-in and library predicates rely on the exception mechanism, so it is usually a bad idea to let Pattern be a variable, matching any exception. If it must be a variable, the Handler should examine the exception and pass it on if it is not relevant to the current invocation.
In a development system, any previously uncaught exception is
caught and an appropriate error message is printed before returning to
the top-level. In recursive calls to Prolog from C, uncaught exceptions
are returned back to C instead. The printing of these and other
messages in a development system is handled by the predicate
print_message/2
(see ref-msg).
catch(
ProtectedGoal,
ExceptionCode,
Handler)
is the same as
on_exception(
ExceptionCode,
ProtectedGoal,
Handler)
.