Tutorial on Error Trapping in APL2/PC
Vector 6:4, page 105
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 105
Sadly error/event handling is an area of APL which has escaped standardisation as readers of the four previous articles in this series (see Vectors 6.1 and 6.3) will know. There is no logical reason for this - compare for example ON units in PL/1. Because of the divergences of event-handling philosophy between different products, comparisons of system functions and variables are at best approximate, but with this proviso, and armed with the information in the previous articles a table of rough correspondences is as follows (APL2 refers to the IBM mainframe and PC products):
| STSC | 68000 | Dyalog | IPSA | APL2 | |
|---|---|---|---|---|---|
| Predetermined Event Action | ⎕ELX |
⎕ERX |
⎕TRAP |
⎕trap ⎕ec |
⎕EA ⎕EC |
| Saved Event Message | ⎕DM |
⎕ERM |
⎕DM/⎕EM |
⎕er |
⎕EM |
| Numeric Event Code | ⎕LER |
⎕EN |
⎕ET |
||
| Event Simulation | ⎕ERROR |
⎕ERS |
⎕SIGNAL |
⎕signal |
⎕ES |
Note that ⎕ec (environment condition) in IPSA is quite different from ⎕EC (execute controlled) in APL2.
Next observe that in APL2 ⎕EM stands for "Event Message" as opposed to mere "Error Message," thereby indicating that the power of the associated facilities goes far beyond that of error handling. In Vector 6.3 Peter Biddlecombe stresses this point on behalf of the analogous facilities in IPSA. An Event is either an Interrupt or an Error, and Errors can arise either as a result of statement execution or the issue of a system command.
Predetermined Event Action is often used as a mechanism for rerouting. APL2 offers it at the statement level, 68000 at the function level, STSC, Dyalog and IPSA at the global, i.e. workspace level, or at the function level through localisation of ⎕ELX, ⎕TRAP, ⎕trap and ⎕ec.
In every case the event message is available either as a function in the case of STSC, 68000 and Dyalog, or as a matrix in the case of IPSA and APL2. Dyalog offers it as a nested vector (⎕DM) as well as as the result of the function ⎕EM applied with ⎕EN as argument. 68000, Dyalog and APL2 offer an explicit coded numeric form, either as a scalar (⎕EN), a 2-item vector (⎕ET) or a function (⎕LER). 68000 additionally gives a line number as the second item of the result of ⎕LER.
All systems provide the ability to simulate events - Dyalog, IPSA and APL2 all provide the capability of defining user event-types by number.
There is little consistency between products concerning the code numbers given to the various error types. What one might call the "seven deadly sins" of APL, do have a common numbering across 68000 and Dyalog, viz.
| 1 | WS FULL |
| 2 | SYNTAX |
| 3 | INDEX |
| 4 | RANK |
| 5 | LENGTH |
| 6 | VALUE |
| 11 | DOMAIN |
(WS FULL is clearly Gluttony, and SYNTAX ERROR Sloth, thereafter the allocation of the remainder becomes more idiosyncratic!) The APL2 system of coding is quite different - viz.:
- Resource Errors :
- 1 1 INTERRUPT
1 2 SYSTEM ERROR
1 3 WS FULL
1 4 thru 1 12 SYSTEM LIMIT of various sorts - SYNTAX ERROR :
- 2 1 thru 2 5
- VALUE ERROR :
- 3 1 Name with no value
3 2 Function with no result - Implicit Argument Error :
- 4 1 thru 4 5 and 4 7 corresponding to
⎕PP,⎕IO,⎕CT,⎕FC,⎕RL,⎕PRrespectively. - Explicit Argument Error :
- 5 1 VALENCE ERROR
5 2 RANK ERROR
5 3 LENGTH ERROR
5 4 DOMAIN ERROR
5 5 INDEX ERROR
5 6 AXIS ERROR
The primary tools for dealing with events in APL2 are ⎕EA and ⎕EC.
Execute Alternate¶
⎕EA gives the user the opportunity to try out the right argument as an execution string, and if this fails, withdraw and try something else. It can also be used to eliminate testing and branching, e.g. to assign a default value of 10 to T use
Here is an example where a function MEAN is intended to return the mean of a frequency table, which is modelled as a matrix with the first column representing values and the second frequencies:
One way to process a vector right argument is to write an alternative function MEANV and select it if necessary using ⎕EA:
Suppose the program’s author believes that it is reasonable to anticipate two types of user error, namely those of supplying a vector R instead of a matrix, and of attempting to use the function in an environment with index origin zero. To counter these possibilities and provide a catchall for other user imbecilities he might write a further function MEANA:
[0] Z←MEANA R
[1] '' ⎕EA '→0 Z←MEAN R'
[2] '⎕IO←1' ⎕EA '→0 Z←MEANV R'
[3] 'Z←''CANNOT IDENTIFY MISTAKE''' ⎕EA '→0 Z←MEAN R'
MEANA V
3
⎕IO←0
MEANA F
1.7272727
MEANA 'ABC'
CANNOT IDENTIFY MISTAKE
An alternative form uses ⎕ET to go directly to each particular error action, with the standard APL message being generated in the case of a non-identified error (see line 3):
[0] Z←MEANB R
[1] '' ⎕EA '→0 Z←MEAN R'
[2] →(L1,L2)IF(⊂⎕ET)≡¨(5 2)(5 5)
[3] →0 Z←⎕EM
[4] L1:→0 Z←MEANV R
[5] L2:Z←MEAN⍉R(⎕IO←1)
where IF is defined as
The next example shows the use of ⎕EA and ⎕ET in an operator which returns a 1 or zero depending on whether its argument is in the domain of its operand:
DOM can be extended in an obvious way to deal with the case of dyadic P.
The next example demonstrates the availability of ⎕EM in the course of the alternative action:
[0] Z←STRONGMSG R
[1] Z←',[⍳0]6⍴⊂↑⊂[2]⎕EM' ⎕EA R
STRONGMSG'3÷0'
DOMAIN ERROR
DOMAIN ERROR
DOMAIN ERROR
DOMAIN ERROR
DOMAIN ERROR
DOMAIN ERROR
Execute Controlled¶
The result of ⎕EC is a 3-item vector. For example
RC (Return Code) has one of 6 values, viz:
| 0 | Error |
| 1 | Normal |
| 2 | Assignment |
| 3 | No result, e.g. ⍎'' |
| 4 | Branch |
| 5 | Abort, i.e. → |
Note that unlike the situation with shared variables a zero return code indicates the presence rather than the absence of an error.
ET is the event type (see above for codes),
Z is one of :
Result (if right argument is valid APL string)
Empty if right argument has no result
⎕EMotherwise.
Continuing the first example, ⎕EC can be used to provide a universal mean function which protects the user from APL error messages as follows:
[0] Z←MEANU R;RC;ET
[1] →0 IF 0≠↑(RC ET Z)←⎕EC 'MEAN R'
[2] →0 IF 0≠↑(RC ET Z)←⎕EC 'MEANV R'
[3] ⎕IO←1
[4] →0 IF 0≠↑(RC ET Z)←⎕EC 'MEAN R'
[5] 'CANNOT IDENTIFY MISTAKE'
⎕IO←1
MEANU ⍳5
3
⎕IO←0
MEANU F
1.7272727
MEANU 2 3 2⍴F
CANNOT IDENTIFY MISTAKE
Event Simulation¶
An event can be simulated by issuing e.g.
where the right argument is any 2-item vector of non-negative integers which is immediately assigned to ⎕ET. The vector may be, but does not need to be one of the error codes listed earlier. If it is not we have a user-defined event. Data may thus be vetted by mapping some anticipated condition, e.g. L is not a matrix, into an event type (either error or user-defined), leading to the appearance of code such as the following at the head of functions:
If the condition fails (i.e. L is a matrix), ⎕ES returns the value ⍳0 and control passes to the next line. The combination of ⎕ES, ⎕EA and ⎕EC provide a more general facility than mere rerouting. Here is a simple example comprising a pair of functions in which a valence error (code = 5 1) is simulated if the ultimate result is less than 10. The alternative action in line 1 of FN1 is for FN1 itself to be re-executed.
⎕CR¨'FN1' 'FN2'
Z←FN1
Z←'FN1'⎕EA'FN2 ',⍞
Z←FN2 R
Z←R
→0 IF Z<10
⎕ES 5 1
FN1
2+2 ⍝ no event simulated
4
W←'ABC'
FN1
9+9 ⍝ valence error simulated
8+8 ⍝ ... and again
W ⍝ ... now a genuine error
2+2
4
⎕ES also has a dyadic form which allows a tailored event message as the left argument. For example the test for L a matrix given above would more usefully be written
⎕ERS in 68000 behaves in a similar way to IPSA’s ⎕signal and APL2’s ⎕ES. Dyalog’s ⎕SIGNAL is similar to monadic ⎕ES. ⎕ERROR in STSC is also monadic but its argument is a character string like the left argument of ⎕ES - the action following ⎕ERROR depends on the setting of ⎕ELX.
Suspension and Control following an Event¶
Any APL interpreter detecting an error goes through a sequence of
(a) detection
(b) reporting
(c) return of control
So far we have discussed the possibilities for user intervention between (a) and (b). Intervention is also possible between (b) and (c).
In APL2 the rule is that control returns up the calling chain to the last point where a ⎕EA or a ⎕EC is in place. If there is none then it cascades to the topmost function in the chain. This give great flexibility as in the example below where the user types REP to obtain a selection of (in this case two) report functions REP1 and REP2. In the first of these there is no ⎕EA trap, and so following an error control passes back to the master function. In the second it is trapped at the level of the report function. Deliberate and obvious errors are sown in REP1 and REP2 - in practice errors would be more subtle and occur following chains of function calls, however provided that there are no intervening ⎕EA or ⎕EC’s the resulting action would be identical in terms of flow of control. In both cases APL error messages are concealed from the user.
OPT←'REP1' 'REP2'
[0] REP R
[1] '''NEXT'''⎕EA⊃OPT[R]
[0] REP1
[1] 'REPORT1
[0] REP2
[1] '''NEXT2'''⎕EA'''REPORT2'
REP 1
NEXT
REP 2
NEXT2
⎕EA and ⎕EC thus behave in the same fashion as the "Cutback" option of Dyalog’s ⎕TRAP or IPSA’s ⎕trap.
Distinguishing Simulated Events from Errors¶
First notice that ⎕EM and ⎕ET are automatically localised within every function so that their current values relate to the function currently at the top of the execution stack. Automatic localisation of ⎕EM and ⎕ET means when simulating events one does not necessarily have to forfeit the power of detecting genuine APL execution errors. In the following development of an earlier example a non-simulated error results in the corresponding values of ⎕EM and ⎕ET being available in the final lines of FN1.
⎕CR¨'FN1' 'FN2'
Z←FN1
L1:'→TEST' ⎕EA 'FN2 ',⍞
→0
TEST:→L1 IF 5 1≡⎕ET
⎕EM
Z←FN2 R
Z←R
→0 IF Z<10
⎕ES 5 1
W←'ABC'
FN1
8+8 ⍝ event 5 1 simulated
W ⍝ event 5 4 genuine error
DOMAIN ERROR
FN2[2] →0 IF Z<10
^ ^
FN2 W
^
Variations in APL2/PC¶
In APL2/PC the ⎕EM and ⎕ET resulting from the right argument of ⎕EA are never placed on the execution stack, and are thus available only during execution of the left argument. If they are to be used in a rerouting sequence initiated by the left argument, they must be assigned to an ordinary variable before the execution of the left argument is complete and before any further errors occur whichever is the soonest. For example if in the function STRONGMSG some processing was required to intervene between the ⎕EA line and the use of ⎕EM then it would be necessary to asssign it in the following fashion:
[0] Z←STRONGMSG R;EM
[1] Z←'→MSG,0⍴EM←⎕EM' ⎕EA R
[2] →0 Z←'CLEAN FINISH'
[3] MSG:Z←'IT WAS A'(,[⍳0]6⍴⊂↑⊂[2]EM)
STRONGMSG'3÷0'
IT WAS A DOMAIN ERROR
DOMAIN ERROR
DOMAIN ERROR
DOMAIN ERROR
DOMAIN ERROR
DOMAIN ERROR
Using Event Handling to Check Domains¶
Dave Ziemann emphasised in his article on a Direct Definition Handler in Vector 6.1 that the power of the Event Trapping facilities in all the systems reviewed goes far beyond that of error detection and control. As a further illustration this section shows how ⎕ES can be used to provide the equivalent of compiled language declarations for function arguments and results. As well as the traditional attributes such as char and integer, APL2 objects have the additional attributes of shape and depth, so that an analogue of an set of internal variable attributes in a compiled language might be
which describes a simple matrix of positive integers. A reasonable standardisation for commenting functions might define domains in the first line and semantics in the second, viz.:
which would indicate that the left argument is a simple character array, the right argument is a numeric scalar or vector of indefinite depth, and the result is a single bit.
It is a small (?) step to translate a line such as the above into a sequence of ⎕ES statements, with those appropriate to L and R at the head of the function, and those for Z immediately prior to the point or points of exit. As an example suppose that a function PLUS is designed to add numeric left arguments to character string integer right arguments with the proviso that no item in the result should exceed 10. The basic function is
Suitably dressed with domain checks this becomes
[0] Z←L PLUS R
[1] 'L OUTSIDE DOMAIN' ⎕ES 9 0 IF~∧/(NUM L),(2>⍴⍴L),2>≡L
[2] 'R OUTSIDE DOMAIN' ⎕ES 9 0 IF~∧/(NCHAR R),((⍴⍴L)=⍴⍴R),2>≡R
[3] Z←L+⍎R
[4] 'Z OUTSIDE DOMAIN' ⎕ES 9 0 IF~∧/(NUM Z),(∧/Z≤10),(2>⍴⍴Z),2>≡Z
[0] Z←NUM R
[1] Z←0=↑0⍴∊R
[0] Z←NCHAR R
[1] Z←∧/R∊' 0123456789'
2 3 PLUS '4 5'
6 8
2 3 PLUS '4 8'
Z OUTSIDE DOMAIN
2 3 PLUS '4 8'
^ ^
2 PLUS '4 5'
R OUTSIDE DOMAIN
2 PLUS '4 5'
^ ^
'2 3' PLUS '4 5'
L OUTSIDE DOMAIN
'2 3' PLUS '4 5'
^ ^
Summary¶
The previous writers in this series have commented on the combination of power with simplicity which the comparatively small number of event handling facilities in their various systems offer. I endorse this on behalf of APL2, while adding a note of regret on the unnecessary variety which exists amongst different systems.