Protecting a Particular Goal

The built-in predicate catch/3 enables you to handle exceptions to a specific goal:

catch(:ProtectedGoal, ?ExceptionTerm, :Handler)   ISO

ProtectedGoal is executed. If all goes well, then it will behave just as if you had written call(ProtectedGoal) instead. If an exception is thrown while ProtectedGoal is running, then 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), then 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 thrown with the ExceptionTerm argument. If this unification succeeds, then Handler will be executed as if you had written

ExceptionTerm=<the actual exception term>,

If this unification fails, then Prolog will keep searching up the ancestor list looking for another exception handler. If during this search it reaches a recursive call to Prolog from C, then the recursive calls returns with an uncaught exception. If it reaches the top-level (or a break level), then 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, then the call to catch/3 is also determinate.

The ProtectedGoal is logically inside the catch/3 goal, but the Handler is not. If an exception is thrown inside the Handler, then this catch/3 goal will not be reactivated. If you want an exception handler that protects itself, then you have to program it, perhaps like this:

recursive_catch_handler(Err, Goal, Handler) :-
    catch(Goal, Err,
        recursive_catch_handler(Err, Handler, Handler)).

See mpg-ref-catch.

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, then the Handler should examine the exception and pass it on if it is not relevant to the current invocation.

Send feedback on this subject.