prologiso-prolog

Does Prolog have a condition and restart system like Common Lisp?


Common Lisp allows exception handling through conditions and restarts. In rough terms, when a function throws an exception, the "catcher" can decide how/whether the "thrower" should proceed. Does Prolog offer a similar system? If not, could one be built on top of existing predicates for walking and examining the call stack?


Solution

  • The ISO/IEC standard of Prolog provides only a very rudimentary exception and error handling mechanism which is - more or less - comparable to what Java offers and far away from Common Lisp's rich mechanism, but there are still some points worth noting. In particular, beside the actual signalling and handling mechanism, many systems provide a mechanism similar to unwind-protect. That is, a way to ensure that a goal will be executed, even in the presence of otherwise unhandled signals.

    ISO throw/1, catch/3

    An exception is raised/thrown with throw(Term). First a copy of Term is created with copy_term/2 lets call it Termcopy and then this new copy is used to search for a corresponding catch(Goal, Pattern, Handler) whose second argument unifies with Termcopy. When Handler is executed, all unifications caused by Goal are undone. So there is no way for the Handler to access the substitutions present when throw/1 is executed. And there is no way to continue at the place where the throw/1 was executed.

    Errors of built-in predicates are signaled by executing throw(error(Error_term, Imp_def)) where Error_term corresponds to one of ISO's error classes and Imp_def may provide implementation defined extra information (like source file, line number etc).

    There are many cases where handling an error locally would be of great benefit but it is deemed by many implementors to be too complex to implement.

    The additional effort to make a Prolog processor handle each and every error locally is quite considerable and is much larger than in Common Lisp or other programming languages. This is due to the very nature of unification in Prolog. The local handling of an error would require to undo unifications performed during the execution of the built-in: An implementor has thus two possibilities to implement this:

    Similar complexities are caused by exploiting WAM registers within built-ins. Again, one has the choice between a slow system or one with significant implementation overhead.

    exception_handler/3

    Many systems, however, provide internally better mechanisms, but few offer them consistently to the programmer. IF/Prolog provides exception_handler/3 which has the same arguments as catch/3 but handles the error or exception locally:

    [user] ?- catch((arg(a,f(1),_); Z=ok), error(type_error(_,_),_), fail).
    
    no
    
    [user] ?- exception_handler((arg(a,f(1),_); Z=ok), error(type_error(_,_),_), fail).
    
    Z       = ok
    
    yes
    

    setup_call_cleanup/3

    This built-in offered by quite a few systems. It is very similar to unwind-protect but requires some additional complexity due to Prolog's backtracking mechanism. See its current definition.


    All these mechanisms need to be provided by the system implementor, they cannot be built on top of ISO Prolog.