- Documentation
- Reference manual
- Built-in Predicates
- Notation of Predicate Descriptions
- Character representation
- Loading Prolog source files
- Editor Interface
- List the program, predicates or clauses
- Verify Type of a Term
- Comparison and Unification of Terms
- Control Predicates
- Meta-Call Predicates
- Delimited continuations
- Exception handling
- Handling signals
- DCG Grammar rules
- Database
- Declaring predicate properties
- Examining the program
- Input and output
- Status of streams
- Primitive character I/O
- Term reading and writing
- Analysing and Constructing Terms
- Analysing and Constructing Atoms
- Localization (locale) support
- Character properties
- Operators
- Character Conversion
- Arithmetic
- Misc arithmetic support predicates
- Built-in list operations
- Finding all Solutions to a Goal
- Forall
- Formatted Write
- Global variables
- Terminal Control
- Operating System Interaction
- File System Interaction
- User Top-level Manipulation
- Creating a Protocol of the User Interaction
- Debugging and Tracing Programs
- Obtaining Runtime Statistics
- Execution profiling
- Memory Management
- Windows DDE interface
- Miscellaneous
- Built-in Predicates
- Packages
- Reference manual
4.11 Exception handling
The predicates catch/3 and throw/1 provide ISO compliant raising and catching 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 comparable to call/1. Recovery from an exception is much slower, especially if the exception term is large due to the copying thereof or is decorated with a stack trace using, e.g., the library
library(prolog_stack)
based on the prolog_exception_hook/4 hook predicate to rewrite exceptions. - [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 that throw/1 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 backtracking until it actually finds 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 shared variables. We assume this to be highly unlikely and could not think of a scenario where this is useful.68I'd like to acknowledge Bart Demoen for his clarifications on these matters.
In addition to explicit calls to throw/1, many built-in predicates throw exceptions directly from C. If the Exception term cannot be copied due to lack of stack space, the following actions are tried in order:
- If the exception is of the form
error(Formal, ImplementationDefined)
, try to raise the exception without the ImplementationDefined part. - Try to raise
error(
.resource_error(stack)
, global) - Abort (see abort/0).
If an exception is raised in a call-back from C (see chapter 11) and not caught in the same call-back, PL_next_solution() fails and the exception context can be retrieved using PL_exception().
- If the exception is of the form
4.11.1 Urgency of exceptions
Under some conditions an exception may be raised as a result of handling another exception. Below are some of the scenarios:
- The predicate setup_call_cleanup/3 calls the cleanup handler as a result of an exception and the cleanup handler raises an exception itself. In this case the most urgent exception is propagated into the environment.
- Raising an exception fails due to lack of resources, e.g., lack of
stack space to store the exception. In this case a resource exception is
raised. If that too fails the system tries to raise a resource exception
without (stack) context. If that fails it will raise the exception
'$aborted'
, also raised by abort/0. As no stack space is required for processing this atomic exception, this should always succeed. - Certain callback operations raise an exception while processing another exception or a previous callback already raised an exception before there was an opportunity to process the exception. The most notable callback subject to this issue are prolog_event_hook/1 (supporting e.g., the graphical debugger), prolog_exception_hook/4 (rewriting exceptions, e.g., by adding context) and print_message/2 when called from the core facilities such as the internal debugger. As with setup_call_cleanup/3, the most urgent exception is preserved.
If the most urgent exceptions needs to be preserved, the following exception ordering is respected, preserving the topmost matching error.
'$aborted'
(abort/0)time_limit_exceeded
(call_with_time_limit/2)error(
resource_error(Resource)
, Context)error(Formal, Context)
- All other exceptions
Note The above resolution is not described in the ISO
standard. This is not needed either because ISO does not specify
setup_call_cleanup/3
and does not deal with environment management issues such as (debugger)
callbacks. Neither does it define abort/0
or timeout handling. Notably abort/0
and timeout are non-logical control structures. They are implemented on
top of exceptions as they need to unwind the stack, destroy choice
points and call cleanup handlers in the same way. However, the pending
exception should not be replaced by another one before the intended
handler is reached. The abort exception cannot be caught, something
which is achieved by wrapping the cleanup handler of catch/3
into
call_cleanup(Handler, abort)
.
4.11.2 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 a non-interactive call-back 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 analyse 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.11.3 The exception term
Built-in predicates generate 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.11.4 Printing messages
The predicate print_message/2 is 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. A 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 define 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 by the system and libraries to print messages. Kind
describes the nature of the message, while
Term is a Prolog term that describes the content. Printing
messages through this indirection instead of using format/3
to the stream
user_error
allows displaying the message appropriate to the application (terminal, logfile, graphics), acting on messages based on their content instead of a string (see message_hook/3) and creating language specific versions of the messages. See also section 4.11.4.1. The following message kinds are known:- banner
- The system banner message. Banner messages can be suppressed by setting
the Prolog flag verbose
to
silent
. - debug(Topic)
- Message from library(debug). See debug/3.
- error
- The message indicates an erroneous situation. This kind is used to print
uncaught exceptions of type
error(Formal, Context)
. See section introduction (section 4.11.4). - help
- User requested help message, for example after entering `h' or `?' to a prompt.
- information
- Information that is requested by the user. An example is statistics/0.
- informational
- Typically messages of events are progres that are considered useful to a
developer. Such messages can be suppressed by setting the Prolog flag verbose
to
silent
. - silent
- Message that is normally not printed. Applications may define message_hook/3 to act upon such messages.
- trace
- Messages from the (command line) tracer.
- warning
- The message indicates something dubious that is not considered fatal. For example, discontiguous predicates (see discontiguous/1).
The predicate print_message/2 first translates the Term into a list of `message lines' (see print_message_lines/3 for details). Next, it calls the hook message_hook/3 to allow the user to intercept the message. If message_hook/3 fails it prints 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 write messages from your own predicates, it is recommended to reuse the existing message terms if applicable. If no existing message term is applicable, invent a fairly unique term that represents the event and define a rule for the multifile predicate prolog:message//1. See section 4.11.4.1 for a deeper discussion and examples.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 arguments. 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. This is combined with a subsequent
message that starts with
at_same_line
to complete the line. - 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
). - ansi(+Attributes, +Format, +Args)
- This message may be intercepted by means of the hook
prolog:message_line_element/2. The library
library(ansi_term)
implements this hook to achieve coloured output. If it is not intercepted it invokesformat(Stream, Format, Args)
. - nl
- A new line is started. If the message is not complete, Prefix is printed before the remainder of the message.
- begin(Kind, Var)
- end(Var)
- The entire message is headed by
begin(Kind, Var)
and ended byend(Var)
. This feature is used by, e.g., librarylibrary(ansi_term)
to colour entire messages. - <Format>
- Handed to format/3
as
format(Stream, Format,[])
. Deprecated because it is ambiguous if Format collides with one of the atomic commands.
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 must be defined dynamic and multifile to allow other modules defining clauses for it too.
- thread_message_hook(+Term, +Kind, +Lines)
- As message_hook/3, but this predicate is local to the calling thread (see thread_local/1). This hook is called before message_hook/3. The `pre-hook' is indented to catch messages they may be produced by calling some goal without affecting other threads.
- message_property(+Kind, ?Property)
- This hook can be used to define additional message kinds and the way
they are displayed. The following properties are defined:
- color(-Attributes)
- Print message using ANSI terminal attributes. See ansi_format/3
for details. Here is an example, printing help messages in blue:
:- multifile user:message_property/2. user:message_property(help, color([fg(blue)])).
- prefix(-Prefix)
- Prefix printed before each line. This argument is handed to format/3.
The default is
'~N'
. For example, messages of kindwarning
use'~NWarning: '
. - location_prefix(+Location, -FirstPrefix, -ContinuePrefix)
- Used for printing messages that are related to a source location.
Currently, Location is a term File:Line.
FirstPrefix is the prefix for the first line and
-ContinuePrefix is the prefix for continuation lines. For
example, the default for errors is
location_prefix(File:Line, '~NERROR: ~w:~d:'-[File,Line], '~N\t')).
- stream(-Stream)
- Stream to which to print the message. Default is
user_error
. - wait(-Seconds)
- Amount of time to wait after printing the message. Default is not to wait.
- prolog:message_line_element(+Stream, +Term)
- This hook is called to print the individual elements of a message from
print_message_lines/3.
This hook is used by e.g., library
library(ansi_term)
to colour messages on ANSI-capable terminals. - message_to_string(+Term, -String)
- Translates a message term into a string object (see section 5.2).
- version
- Write the SWI-Prolog banner message as well as additional messages registered using version/1. This is the default initialization goal which can be modified using -g.
- version(+Message)
- Register additional messages to be printed by version/0. Each registered message is handed to the message translation DCG and can thus be defined using the hook prolog:message//1. If not defined, it is simply printed.
4.11.4.1 Printing from libraries
Libraries should not use format/3
or other output predicates directly. Libraries that print informational
output directly to the console are hard to use from code that depend on
your textual output, such as a CGI script. The predicates in section
4.11.4 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 the 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., to 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 command line option -q, the user can disable all informational messages.