## 4.9 ISO compliant Exception handling

SWI-Prolog defines the predicates catch/3 and throw/1 for ISO compliant raising and catching of exceptions. In the current implementation (4.0.6), most of the built-in predicates generate exceptions, but some obscure predicates merely print a message, start the debugger and fail, which was the normal behaviour before the introduction of exceptions.

[ISO]catch(:Goal, +Catcher, :Recover)
  Behaves as call/1 if no exception is raised when executing Goal. If an exception is raised using throw/1 while Goal executes, and the Goal is the innermost goal for which Catcher unifies with the argument of throw/1, all choice-points generated by Goal are cut, the system backtracks to the start of catch/3 while preserving the thrown exception term and Recover is called as in call/1.

  The overhead of calling a goal through catch/3 is very comparable to call/1. Recovery from an exception is much slower, especially if the exception-term is large due to the copying thereof.

[ISO]throw(+Exception)
  Raise an exception. The system looks for the innermost catch/3 ancestor for which Exception unifies with the Catcher argument of the catch/3 call. See catch/3 for details.

  ISO demands throw/1 to make a copy of Exception, walk up the stack to a catch/3 call, backtrack and try to unify the copy of Exception with Catcher. SWI-Prolog delays making a copy of Exception and backtracking until it actually found a matching catch/3 goal. The advantage is that we can start the debugger at the first possible location while preserving the entire exception context if there is no matching catch/3 goal. This approach can lead to different behaviour if Goal and Catcher of catch/3 call share variables. We assume this to be highly unlikely and could not think of a scenario where this is useful.33I'd like to acknowledge Bart Demoen for his clarifications on these matters.

  If an exception is raised in a callback from C (see chapter 9) and not caught in the same call-back, PL_next_solution() fails and the exception context can be retrieved using PL_exception().

### 4.9.1 Debugging and exceptions

Before the introduction of exceptions in SWI-Prolog a runtime error was handled by printing an error message, after which the predicate failed. If the Prolog flag debug_on_error was in effect (default), the tracer was switched on. The combination of the error message and trace information is generally sufficient to locate the error.

With exception handling, things are different. A programmer may wish to trap an exception using catch/3 to avoid it reaching the user. If the exception is not handled by user-code, the interactive top-level will trap it to prevent termination.

If we do not take special precautions, the context information associated with an unexpected exception (i.e., a programming error) is lost. Therefore, if an exception is raised, which is not caught using catch/3 and the top-level is running, the error will be printed, and the system will enter trace mode.

If the system is in an non-interactive callback from foreign code and there is no catch/3 active in the current context, it cannot determine whether or not the exception will be caught by the external routine calling Prolog. It will then base its behaviour on the Prolog flag debug_on_error:

- current_prolog_flag(debug_on_error, false)
  The exception does not trap the debugger and is returned to the foreign routine calling Prolog, where it can be accessed using PL_exception(). This is the default.
- current_prolog_flag(debug_on_error, true)
  If the exception is not caught by Prolog in the current context, it will trap the tracer to help analysing the context of the error.

While looking for the context in which an exception takes place, it is advised to switch on debug mode using the predicate debug/0. The hook prolog_exception_hook/4 can be used to add more debugging facilities to exceptions. An example is the library `library(http/http_error)`, generating a full stack trace on errors in the HTTP server library.

### 4.9.2 The exception term

Built-in predicates generates exceptions using a term `error(Formal, Context)`. The first argument is the `formal' description of the error, specifying the class and generic defined context information. When applicable, the ISO error-term definition is used. The second part describes some additional context to help the programmer while debugging. In its most generic form this is a term of the form `context(Name/Arity, Message)`, where Name/Arity describes the built-in predicate that raised the error, and Message provides an additional description of the error. Any part of this structure may be a variable if no information was present.

### 4.9.3 Printing messages

The predicate print_message/2 may be used to print a message term in a human readable format. The other predicates from this section allow the user to refine and extend the message system. The most common usage of print_message/2 is to print error messages from exceptions. The code below prints errors encountered during the execution of Goal, without further propagating the exception and without starting the debugger.

            ...,
            catch(Goal, E,
                  ( print_message(error, E),
                    fail
                  )),
            ...

Another common use is to defined message_hook/3 for printing messages that are normally silent, suppressing messages, redirecting messages or make something happen in addition to printing the message.

print_message(+Kind, +Term)
  The predicate print_message/2 is used to print messages, notably from exceptions in a human-readable format. Kind is one of `informational`, `banner`, `warning`, `error`, `help` or `silent`. A human-readable message is printed to the stream `user_error`.

  If the Prolog flag verbose is `silent`, messages with Kind `informational`, or `banner` are treated as silent. See -q.

  This predicate first translates the Term into a list of `message lines' (see print_message_lines/3 for details). Next it will call the hook message_hook/3 to allow the user intercepting the message. If message_hook/3 fails it will print the message unless Kind is silent.

  The print_message/2 predicate and its rules are in the file `<plhome>/boot/messages.pl`, which may be inspected for more information on the error messages and related error terms. If you need to report errors from your own predicates, we advise you to stick to the existing error terms if you can; but should you need to invent new ones, you can define corresponding error messages by asserting clauses for prolog:message. You will need to declare the predicate as multifile.

  See also message_to_string/2.

print_message_lines(+Stream, +Prefix, +Lines)
  Print a message (see print_message/2) that has been translated to a list of message elements. The elements of this list are:
  <Format>-<Args>
    Where Format is an atom and Args is a list of format argument. Handed to format/3.
  `flush`
    If this appears as the last element, Stream is flushed (see flush_output/1) and no final newline is generated.
  `at_same_line`
    If this appears as first element, no prefix is printed for the first line and the line-position is not forced to 0 (see format/1, `~N`).
  <Format>
    Handed to format/3 as `format(Stream, Format, [])`.
  nl
    A new line is started and if the message is not complete the Prefix is printed too.

  See also print_message/2 and message_hook/3.

message_hook(+Term, +Kind, +Lines)
  Hook predicate that may be defined in the module `user` to intercept messages from print_message/2. Term and Kind are the same as passed to print_message/2. Lines is a list of format statements as described with print_message_lines/3. See also message_to_string/2.

  This predicate should be defined dynamic and multifile to allow other modules defining clauses for it too.

message_to_string(+Term, -String)
  Translates a message-term into a string object (see section 4.22). Primarily intended to write messages to Windows in XPCE (see section 1.5) or other GUI environments.

#### 4.9.3.1 Printing from libraries

Libraries should not use format/3 or other output predicates directly. Libraries that print informational output directory to the console are hard to use from code that depend on your textual output, such as a GI script. The predicates in section 4.9.3 define the API for dealing with messages. The idea behind this is that a library that wants to provide information about its status, progress, events or problems calls print_message/2. The first argument is the level. The supported levels are described with print_message/2. Libraries typically use `informational` and `warning`, while libraries should use exceptions for errors (see throw/1, type_error/2, etc.).

The second argument is an arbitrary Prolog term that carries te information of the message, but not the precise text. The text is defined by the grammar rule prolog:message//1. This distinction is made to allow for translations and to allow hooks processing the information in a different way (e.g., translate progress messages into a progress-bar).

For example, suppose we have a library that must download data from the internet (e.g., based on http_open/3). The library wants to print the progress after each downloaded file. The code below is a good skeleton:

    download_urls(List) :-
            length(List, Total),
            forall(nth1(I, List, URL),
                   (   download_url(URL),
                       print_message(informational,
                                     download_url(URL, I, Total)))).

The programmer can now specify the default textual output using the rule below. Note that this rule may be in the same file or anywhere else. Notably, the application may come with several rule-sets for different languages. This, and the user-hook example below are the reason to represent the message as a compound term rather than a string. This is similar to using message-numbers in non-symbolic languages. The documentation of print_message_lines/3 describes the elements that may appear in the output list.

    :- multifile
            prolog:message//1.

    prolog:message(download_url(URL, I, Total)) -->
            { Perc is round(I*100/Total) },
            [ 'Downloaded ~w; ~D from ~D (~d%)'-[URL, I, Total, Perc] ].

A user of the library may define rules for message_hook/3. The rule below acts on the message-content. Other applications can act on the message-level and, for example, popup a message-box for warnings and errors.

    :- multifile user:message_hook/3.

    message_hook(download_url(URL, I, Total), _Kind, _Lines) :-
            <send this information to a GUI component>

In addition, using the commandline option -q, the user can disable all informational messages.