Be aware that all error terms are of the shape
error(Formal, ImplementationDefined). The Formal part is
resource_error(program_space). The ImplementationDefined is typically left a variable in SWI-Prolog and bound to a stack trace if the exception is uncaught or caught by
catch_with_backtrace/3. Typically, if you want to catch a specific error you use
catch(Goal, error(resource_error(program_space), _), <recover>).
If you want to print, a common pattern is the one below. The
Error = error(_,_) prevents trapping special exceptions such as from
Error = error(_,_),
catch(Goal, Error, (print_message(error, Error), fail)),
In general though, SWI-Prolog deals poorly with really running out of memory (as in C malloc() returning
NULL) and in most places reports this as a fatal error and dies. Newer code often throws
resource_error(memory), but soon enough you’ll bump into older code that kills the process.
program_space is property of a module that is, for example, used by SWISH to avoid SWISH users crashing the server by making it run out of memory using e.g., assert/1.
There is a lot more to say about handling errors. In general I do not like the current situation much. In the XPCE graphics systems there are three types of errors: fatal errors that require termination, programming errors that are (99%) caused by a broken program such as typing errors or existence errors of classes and methods and environment errors that are typically caused by e.g., missing files, files containing invalid data), etc.
As is, for example if you try to open and read a file, there are a lot of different environment errors possible: existence error of the file, permission errors, representation errors for (Unicode) characters and I/O errors (last two not formally described by ISO AFAIK). But, such a piece of code may also be subject to all sorts of programming errors such as instantiation and type errors. In the first class you want to inform the application user as good and human friendly as possible what is wrong. In the second class you want to produce an informative error report that the application user can send to the developers.
Coding the above is hard though. You get something like this:
Error = error(Formal, Context),
catch(Goal, Error, true),
-> <ok, go on>
; Formal = existence_error(source_sink, File)
-> <tell user File does not exist>
; <tell user the application is broken and report X to the developers>
Some abstraction can make this a little cleaner, but IMO it stays a mess. I’ve been playing with the idea to consider constraints in the catch/3 ball. That would allow for something like
catch(Goal, Error, <tell user about Error>),
And catch all programming errors on the outside of the entire application. Now we can implement a library that defines a (constraint) taxonomy of errors that need to be handled in a specific way.