Exception Handling

## 4.11 Exception Handling

It is sometimes necessary to exit prematurely from an executing procedure, for example because some situation was detected which makes continuing impossible. In this situation, one wants to return to some defined state and perform some kind of recovery action. This functionality is provided by block/3 and exit_block/1.

> block(Goal, BTag, Recovery) like `call(Goal)`, except that in addition a Recovery goal is set up, which can be called by `exit_block` from anywhere inside the call to Goal. When `exit_block(ETag)` is called, then if `ETag` unifies with a `BTag` from an enclosing `block`, the recovery goal associated with that `block` is called, with the system immediately failing back to where the `block` was called. In addition, `ETag` can be used to pass information to the recovery goal, if `BTag` occurs as an argument of `Recovery`. exit_block(ETag) will transfer control to the innermost enclosing block/3 whose `BTag` argument unifies with `ETag`.

> Figure 4.6: Exception Handling

By wrapping a predicate call into block/3, any irregular termination can be caught and handled, e.g.

>     protected_main(X,Y,Z) :-
>         block(
>             main(X,Y,Z),
>             Problem,
>             printf("Execution of main/3 aborted with %w%n", [Problem])
>         ).

>     main(X,Y,Z) :-
>         ...,
>         ( test(...) -> ... ; exit_block(test_failed) ),
>         ...,

When built-in predicates raise errors, this results in the predicate being exited with the tag `abort`, which can also be caught:

>     ?- block(X is 1//0, T, true).
>     arithmetic exception in //(1, 0, X)
>     X = X
>     T = abort
>     Yes (0.00s cpu)

Note that timeouts and stack overflows also lead to exits and can be caught this way.