Next: , Previous: , Up: Top   [Contents]

12 Exception handling

Mercury procedures may throw exceptions. Exceptions may be caught using the predicates defined in the ‘exception’ library module, or using try goals.

A ‘try’ goal has the following form:

        try Params Goal
        then ThenGoal
        else ElseGoal
        catch Term -> CatchGoal
        catch_any CatchAnyVar -> CatchAnyGoal

Goal, ThenGoal, ElseGoal, CatchGoal, CatchAnyGoal must be valid goals.

Goal must have one of the following determinisms: ‘det’, ‘semidet’, ‘cc_multi’, or ‘cc_nondet’.

The non-local variables of Goal must not have an inst equivalent to ‘unique’ or ‘mostly_unique’ or ‘any’, unless they have the type ‘io.state’.

Params must be a valid list of zero or more try parameters.

The “then” part is mandatory. The “else” part is mandatory if Goal may fail; otherwise it must be omitted. There may be zero or more “catch” branches. The “catch_any” part is optional. CatchAnyVar must be a single variable.

The try parameter ‘io’ takes a single argument, which must be the name of a state variable prefixed by ‘!’; for example, ‘io(!IO)’. The state variable must have the type ‘io.state’, and be in scope of the try goal. The state variable is threaded through ‘Goal’, so it may perform I/O but cannot fail. If no ‘io’ parameter exists, ‘Goal’ may not perform I/O and may fail.

A try goal has determinism ‘cc_multi’.

On entering a try goal, Goal is executed. If it succeeds without throwing an exception, ThenGoal is executed. Any variables bound by Goal are visible in ThenGoal only. If Goal fails, then ElseGoal is executed.

If Goal throws an exception, the exception value is unified with each of the Terms in the “catch” branches in turn. On the first successful unification, the corresponding CatchGoal is executed (and other “catch” and “catch_any” branches ignored). Variables bound during the unification of the Term are in scope of the corresponding CatchGoal.

If the exception value does not unify with any of the terms in “catch” branches, and a “catch_any” branch is present, the exception is bound to CatchAnyVar and the CatchAnyGoal executed. CatchAnyVar is visible in the CatchAnyGoal only, and is existentially typed, i.e. it has type ‘some [T] T’.

Finally, if the thrown value did not unify with any “catch” term, and there is no “catch_any” branch, the exception is rethrown.

The declarative semantics of a try goal is:

        (try [] Goal
         then Then
         else Else
         catch CP1 -> CG1
         catch CG2 -> CG2
         catch_any CAV -> CAG
        )  <=>
                        Goal, Then
                        not Goal, Else
                        some [Excp]
                        ( Excp = CP1 -> CG1
                        ; Excp = CP2 -> CG2
                        ; ...
                        ; Excp = CAV, CAG

If no ‘else’ branch is present, let ‘Else = fail’. If no ‘catch_any’ branch is present, let ‘CAG = fail’.

An example of a try goal that performs I/O is:

:- pred p_carefully(io::di, io::uo) is cc_multi.

p_carefully(!IO) :-
        (try [io(!IO)] (
                io.write_string("Calling p\n", !IO),
                p(Output, !IO)
                io.write_string("p returned: ", !IO),
                io.write(Output, !IO),
        catch S ->
                io.write_string("p threw a string: ", !IO),
                io.write_string(S, !IO),
        catch 42 ->
                io.write_string("p threw 42\n", !IO)
        catch_any Other ->
                io.write_string("p threw something: ", !IO),
                io.write(Other, !IO),
                % Rethrow the value.

Next: , Previous: , Up: Top   [Contents]