Skip to content

Error-Guards

An error-guard is (an expression that evaluates to) a vector of error numbers (see APL Error Messages), followed by the digraph: ::, followed by an expression, the body of the guard, to be evaluated as the result of the function. For example:

      11 5 :: ×0 ⍝ Trap DOMAIN and LENGTH errors.

In common with :Trap and ⎕TRAP, error numbers 0 and 1000 are catch-alls for synchronous errors and interrupts respectively.

When an error is generated, the system searches dynamically through the calling functions for an error-guard that matches the error. If one is found, the execution environment is unwound to its state immediately prior to the error-guard's execution and the body of the error-guard is evaluated as the result of the function. This means that, during evaluation of the body, the guard is no longer in effect and so the danger of a hang caused by an infinite "trap loop", is avoided.

Notice that you can provide "cascading" error trapping in the following way:

      0::try_2nd
      0::try_1st
      expr

In this case, if expr generates an error, its immediately preceding: 0:: catches it and evaluates try_1st leaving the remaining error-guard in scope. If try_1st fails, the environment is unwound once again and try_2nd is evaluated, this time with no error-guards in scope.

See also Guards.

Examples

Open returns a handle for a component file. If the exclusive tie fails, it attempts a share-tie and if this fails, it creates a new file. Finally, if all else fails, a handle of 0 is returned.

open{                 ⍝ Handle for component file ⍵.
    0::0               ⍝ Fails:: return 0 handle.
    22::⍵ ⎕FCREATE 0   ⍝ FILE NAME:: create new one.
    24 25::⍵ ⎕FSTIE 0  ⍝ FILE TIED:: try share tie.
            ⎕FTIE 0   ⍝ Attempt to open file.
}

An error in div causes it to be called recursively with improved arguments.

div{                  ⍝ Tolerant division:: ⍺÷0 → ⍺.
    1                ⍝ default numerator.
    5::/↓↑         ⍝ LENGTH:: stretch to fit.
    11::⍺  +=0      ⍝ DOMAIN:: increase divisor.
    ÷                ⍝ attempt division.
}

Notice that some arguments may cause div to recur twice:

       6 4 2 div 3 2
      6 4 2 div 3 2 0
      6 4 2 div 3 2 1
      2 2 2

The final example shows the unwinding of the local environment before the error-guard's body is evaluated. Local name trap is set to describe the domain of its following error-guard. When an error occurs, the environment is unwound to expose trap's statically correct value.

      add{
          trap'domain'  11::trap
          trap'length'   5::trap
          +
      }

      2 add 3         ⍝ Addition succeeds 
5

      2 add 'three'   ⍝ DOMAIN ERROR generated.
domain

      2 3 add 4 5 6   ⍝ LENGTH ERROR generated.
length

Note

Following the setting of an error-guard, subsequent function calls will disable tail call optimisation:

    {
        22::'Oops!'     ⍝ this error-guard means that ...
        tie ⎕ftie 0
        subfn tie       ⍝ ... tail call not optimised
    }

One way to maintain the tail call optimisation in the presence of an error-guard is to isolate it within an inner function:

    {
        tie{
            22::0        ⍝ error-guard local to inner fn
             ⎕ftie 0
        }
        tie=0:'Oops!'
        subfn tie        ⍝ ... so this is a tail call 
    }