Error trapping with Dyalog APL: Difference between revisions

Jump to navigation Jump to search
m
Text replacement - "<source" to "<syntaxhighlight"
m (Text replacement - "</source>" to "</syntaxhighlight>")
m (Text replacement - "<source" to "<syntaxhighlight")
 
Line 22: Line 22:
It is good programming style to avoid using numbers in code. Instead of talking about 1001, for example, we should use a meaningful name:
It is good programming style to avoid using numbers in code. Instead of talking about 1001, for example, we should use a meaningful name:


<source lang=apl>
<syntaxhighlight lang=apl>
⎕CS 'Events' #.⎕NS ''
⎕CS 'Events' #.⎕NS ''
⎕FX 'r←StopVector' 'r←1001'
⎕FX 'r←StopVector' 'r←1001'
Line 33: Line 33:
We need also a user-defined event for restarting the application. This is explained soon:
We need also a user-defined event for restarting the application. This is explained soon:


<source lang=apl>
<syntaxhighlight lang=apl>
#.Events.RestartAppl←501
#.Events.RestartAppl←501
</syntaxhighlight>
</syntaxhighlight>
Line 39: Line 39:
According to the help file, users should use the range from 500 to 999 to define their own events.
According to the help file, users should use the range from 500 to 999 to define their own events.


== Setting <source lang=apl inline>⎕TRAP</syntaxhighlight> ==
== Setting <syntaxhighlight lang=apl inline>⎕TRAP</syntaxhighlight> ==


<source lang=apl inline>⎕TRAP</syntaxhighlight> allows us to implement a general mechanism on a global level. For discussion purposes let's assume the following:
<syntaxhighlight lang=apl inline>⎕TRAP</syntaxhighlight> allows us to implement a general mechanism on a global level. For discussion purposes let's assume the following:
# <source lang=apl inline>⎕LX</syntaxhighlight> is set to run function <source lang=apl inline>Run</syntaxhighlight>
# <syntaxhighlight lang=apl inline>⎕LX</syntaxhighlight> is set to run function <syntaxhighlight lang=apl inline>Run</syntaxhighlight>
# This function calls 3 sub-functions: <source lang=apl inline>Initial</syntaxhighlight>, <source lang=apl inline>Work</syntaxhighlight> and <source lang=apl inline>Shutdown</syntaxhighlight>
# This function calls 3 sub-functions: <syntaxhighlight lang=apl inline>Initial</syntaxhighlight>, <syntaxhighlight lang=apl inline>Work</syntaxhighlight> and <syntaxhighlight lang=apl inline>Shutdown</syntaxhighlight>
# <source lang=apl inline>Initial</syntaxhighlight> initializes the application: it opens files, interprets an INI file, takes the Windows registry into account, builds the GUI and so forth.
# <syntaxhighlight lang=apl inline>Initial</syntaxhighlight> initializes the application: it opens files, interprets an INI file, takes the Windows registry into account, builds the GUI and so forth.
# <source lang=apl inline>Work</syntaxhighlight> simply runs <source lang=apl inline>⎕DQ</syntaxhighlight>
# <syntaxhighlight lang=apl inline>Work</syntaxhighlight> simply runs <syntaxhighlight lang=apl inline>⎕DQ</syntaxhighlight>
# <source lang=apl inline>Shutdown</syntaxhighlight> cleans up: it closes files, says good-by.
# <syntaxhighlight lang=apl inline>Shutdown</syntaxhighlight> cleans up: it closes files, says good-by.


=== Solving the stop vector problem ===
=== Solving the stop vector problem ===


Let's start with solving the stop vector problem:
Let's start with solving the stop vector problem:
<source lang=apl>
<syntaxhighlight lang=apl>
⎕TRAP←⊂(#.Events.StopVector 'E' '→⎕LC')
⎕TRAP←⊂(#.Events.StopVector 'E' '→⎕LC')
</syntaxhighlight>
</syntaxhighlight>
<source lang=apl inline>#.Events.StopVector</syntaxhighlight> returns 1001 which is the event number a stop vector is associated with. As soon as APL stops on a stop vector <source lang=apl inline>⎕EN</syntaxhighlight> is set to 1001. This event can be caught with <source lang=apl inline>⎕TRAP</syntaxhighlight>, so we can tell APL to execute (<source lang=apl inline>'E'</syntaxhighlight>) the expression given as third argument. In this case it tells APL to simply ignore stop vectors by resuming execution.
<syntaxhighlight lang=apl inline>#.Events.StopVector</syntaxhighlight> returns 1001 which is the event number a stop vector is associated with. As soon as APL stops on a stop vector <syntaxhighlight lang=apl inline>⎕EN</syntaxhighlight> is set to 1001. This event can be caught with <syntaxhighlight lang=apl inline>⎕TRAP</syntaxhighlight>, so we can tell APL to execute (<syntaxhighlight lang=apl inline>'E'</syntaxhighlight>) the expression given as third argument. In this case it tells APL to simply ignore stop vectors by resuming execution.


=== Preventing users from interrupting an application ===
=== Preventing users from interrupting an application ===
Line 61: Line 61:


Here, however, we will use this simple approach:
Here, however, we will use this simple approach:
<source lang=apl>
<syntaxhighlight lang=apl>
events←#.Events.WeakInterrupt #.Events.StrongInterrupt
events←#.Events.WeakInterrupt #.Events.StrongInterrupt
⎕TRAP,←⊂(events 'E' '→⎕LC')
⎕TRAP,←⊂(events 'E' '→⎕LC')
Line 68: Line 68:
=== Restarting the application ===
=== Restarting the application ===


For reasons explained in a minute we now have to define the “Restart the application” procedure. For this, for the first time we do not use the <source lang=apl inline>'E'</syntaxhighlight> statement but the <source lang=apl inline>'C'</syntaxhighlight> statement. The ''C'' is short for ''Cut back''. This instructs APL to cut the status indicator back to the level where <source lang=apl inline>⎕TRAP</syntaxhighlight> '''is localized''' – that is not  necessarily where it was set – and execute the expression in the 3rd argument there. However, if <source lang=apl inline>⎕TRAP</syntaxhighlight> is not localized at all, i.e. it is in the workspace, the status indicator is cut back completely and the expression is executed in the workspace.
For reasons explained in a minute we now have to define the “Restart the application” procedure. For this, for the first time we do not use the <syntaxhighlight lang=apl inline>'E'</syntaxhighlight> statement but the <syntaxhighlight lang=apl inline>'C'</syntaxhighlight> statement. The ''C'' is short for ''Cut back''. This instructs APL to cut the status indicator back to the level where <syntaxhighlight lang=apl inline>⎕TRAP</syntaxhighlight> '''is localized''' – that is not  necessarily where it was set – and execute the expression in the 3rd argument there. However, if <syntaxhighlight lang=apl inline>⎕TRAP</syntaxhighlight> is not localized at all, i.e. it is in the workspace, the status indicator is cut back completely and the expression is executed in the workspace.
<source lang=apl>
<syntaxhighlight lang=apl>
⎕TRAP,←⊂(#.Events.RestartAppl 'C' '→∆Restart')
⎕TRAP,←⊂(#.Events.RestartAppl 'C' '→∆Restart')
</syntaxhighlight>
</syntaxhighlight>
To make this work the function in which <source lang=apl inline>⎕TRAP</syntaxhighlight> is localized must have a label <source lang=apl inline>∆Restart</syntaxhighlight> or a function that returns a valid line number to branch to of course.
To make this work the function in which <syntaxhighlight lang=apl inline>⎕TRAP</syntaxhighlight> is localized must have a label <syntaxhighlight lang=apl inline>∆Restart</syntaxhighlight> or a function that returns a valid line number to branch to of course.


=== Catching Errors ===
=== Catching Errors ===


If an unexpected error occurs, we want to execute a particular function to do the hard work.
If an unexpected error occurs, we want to execute a particular function to do the hard work.
<source lang=apl>
<syntaxhighlight lang=apl>
⎕TRAP,←⊂((0 1000) 'E' '#.HandleError' )
⎕TRAP,←⊂((0 1000) 'E' '#.HandleError' )
</syntaxhighlight>
</syntaxhighlight>
The 0 stands for all the events from 1 to 999 while the 1000 stands for all events larger than 1000.
The 0 stands for all the events from 1 to 999 while the 1000 stands for all events larger than 1000.


<source lang=apl inline>⎕TRAP</syntaxhighlight> may contain more than one error catching group. Since the contents of <source lang=apl inline>⎕TRAP</syntaxhighlight> is scanned from left to right, a statement will ONLY be executed for an event not processed earlier. That is the reason why we must define the restart event first.
<syntaxhighlight lang=apl inline>⎕TRAP</syntaxhighlight> may contain more than one error catching group. Since the contents of <syntaxhighlight lang=apl inline>⎕TRAP</syntaxhighlight> is scanned from left to right, a statement will ONLY be executed for an event not processed earlier. That is the reason why we must define the restart event first.


For example, in the following statement:
For example, in the following statement:
<source lang=apl>
<syntaxhighlight lang=apl>
⎕TRAP←(333 'E' 'expA') (0 'E' 'expB')
⎕TRAP←(333 'E' 'expA') (0 'E' 'expB')
</syntaxhighlight>
</syntaxhighlight>
event 333 will be caught by the 1st group and NOT by the 2nd even though 0 stands for “events from 1 to 999”. Only the expression <source lang=apl inline>expA</syntaxhighlight> will be executed.
event 333 will be caught by the 1st group and NOT by the 2nd even though 0 stands for “events from 1 to 999”. Only the expression <syntaxhighlight lang=apl inline>expA</syntaxhighlight> will be executed.


== The <source lang=apl inline>#.HandleError</syntaxhighlight> function ==
== The <syntaxhighlight lang=apl inline>#.HandleError</syntaxhighlight> function ==


The <source lang=apl inline>#.HandleError</syntaxhighlight> function should do at least the following:
The <syntaxhighlight lang=apl inline>#.HandleError</syntaxhighlight> function should do at least the following:


* Perhaps neutralize <source lang=apl inline>⎕LX</syntaxhighlight>
* Perhaps neutralize <syntaxhighlight lang=apl inline>⎕LX</syntaxhighlight>
* Save the <source lang=apl inline>⎕DM</syntaxhighlight> and <source lang=apl inline>⎕EN</syntaxhighlight> settings
* Save the <syntaxhighlight lang=apl inline>⎕DM</syntaxhighlight> and <syntaxhighlight lang=apl inline>⎕EN</syntaxhighlight> settings
* Save the snapshot
* Save the snapshot
* Maybe create an HTML page with general information about the error
* Maybe create an HTML page with general information about the error
Line 110: Line 110:
Keep in mind that you want to have an easy opportunity to test the system with error trapping. So you may need another parameter that tells the system that error trapping has to be used. Last but not least, there should be an easy opportunity to let the application crash on purpose. I prefer to have a “developers menu”, which is displayed only to developers. Among other useful commands it offers a “Let's crash” option.
Keep in mind that you want to have an easy opportunity to test the system with error trapping. So you may need another parameter that tells the system that error trapping has to be used. Last but not least, there should be an easy opportunity to let the application crash on purpose. I prefer to have a “developers menu”, which is displayed only to developers. Among other useful commands it offers a “Let's crash” option.


=== Control Structure <source lang=apl inline>:Trap</syntaxhighlight> ===
=== Control Structure <syntaxhighlight lang=apl inline>:Trap</syntaxhighlight> ===


If you use <source lang=apl inline>:Trap</syntaxhighlight>, keep in mind that <source lang=apl inline>⎕TRAP</syntaxhighlight> and <source lang=apl inline>:Trap</syntaxhighlight> are both taken into account. That means that in case of
If you use <syntaxhighlight lang=apl inline>:Trap</syntaxhighlight>, keep in mind that <syntaxhighlight lang=apl inline>⎕TRAP</syntaxhighlight> and <syntaxhighlight lang=apl inline>:Trap</syntaxhighlight> are both taken into account. That means that in case of
<source lang=apl>
<syntaxhighlight lang=apl>
:Trap 0
:Trap 0
-'a'
-'a'
Line 121: Line 121:
</syntaxhighlight>
</syntaxhighlight>


the error caused by the <source lang=apl inline>-'a'</syntaxhighlight> statement is caught by the <source lang=apl inline>:Else</syntaxhighlight>, while the <source lang=apl inline>.</syntaxhighlight> is caught by the <source lang=apl inline>⎕TRAP</syntaxhighlight> setting.
the error caused by the <syntaxhighlight lang=apl inline>-'a'</syntaxhighlight> statement is caught by the <syntaxhighlight lang=apl inline>:Else</syntaxhighlight>, while the <syntaxhighlight lang=apl inline>.</syntaxhighlight> is caught by the <syntaxhighlight lang=apl inline>⎕TRAP</syntaxhighlight> setting.


When using <source lang=apl inline>:Trap</syntaxhighlight> try to be as specific as possible. For example, this code is faulty:
When using <syntaxhighlight lang=apl inline>:Trap</syntaxhighlight> try to be as specific as possible. For example, this code is faulty:
<source lang=apl>
<syntaxhighlight lang=apl>
:Trap 0
:Trap 0
filename ⎕FTIE 0
filename ⎕FTIE 0
Line 132: Line 132:
</syntaxhighlight>
</syntaxhighlight>
because it tries to create a file not only if this file does not already exist but also if the current user lacks the right to tie it, for example because somebody else has already tied it exclusively. Therefore, it is a better to be specific:
because it tries to create a file not only if this file does not already exist but also if the current user lacks the right to tie it, for example because somebody else has already tied it exclusively. Therefore, it is a better to be specific:
<source lang=apl>
<syntaxhighlight lang=apl>
:Trap 22
:Trap 22
filename ⎕FTIE 0
filename ⎕FTIE 0
Line 139: Line 139:
:EndTrap
:EndTrap
</syntaxhighlight>
</syntaxhighlight>
The best idea, however, is to use <source lang=apl inline>⎕NEXISTS</syntaxhighlight> to check the file for already being created. In general it is a good idea to use error trapping only for extraordinary problems.
The best idea, however, is to use <syntaxhighlight lang=apl inline>⎕NEXISTS</syntaxhighlight> to check the file for already being created. In general it is a good idea to use error trapping only for extraordinary problems.


=== The <source lang=apl inline>⎕SIGNAL</syntaxhighlight> system function ===
=== The <syntaxhighlight lang=apl inline>⎕SIGNAL</syntaxhighlight> system function ===


Note that an event which is <source lang=apl inline>⎕SIGNAL</syntaxhighlight>led can be intercepted with <source lang=apl inline>⎕TRAP</syntaxhighlight> but not <source lang=apl inline>:Trap</syntaxhighlight>
Note that an event which is <syntaxhighlight lang=apl inline>⎕SIGNAL</syntaxhighlight>led can be intercepted with <syntaxhighlight lang=apl inline>⎕TRAP</syntaxhighlight> but not <syntaxhighlight lang=apl inline>:Trap</syntaxhighlight>
If you execute this function:
If you execute this function:
<source lang=apl>
<syntaxhighlight lang=apl>
∇test
∇test
  ⎕TRAP←501 'E' '⎕←''caught by ⎕TRAP'''
  ⎕TRAP←501 'E' '⎕←''caught by ⎕TRAP'''
Line 156: Line 156:
</syntaxhighlight>
</syntaxhighlight>
you get this:
you get this:
<source lang=apl>
<syntaxhighlight lang=apl>
caught by ⎕TRAP
caught by ⎕TRAP
</syntaxhighlight>
</syntaxhighlight>
Line 163: Line 163:


A very easy way to create problems in the future is to do this:
A very easy way to create problems in the future is to do this:
<source lang=apl>
<syntaxhighlight lang=apl>
:Trap 0
:Trap 0
DoSomethingHere
DoSomethingHere
Line 174: Line 174:
When you use error trapping, make sure that you can switch off error trapping on a general level. The easiest way to implement this idea is something like this:
When you use error trapping, make sure that you can switch off error trapping on a general level. The easiest way to implement this idea is something like this:


<source lang=apl>
<syntaxhighlight lang=apl>
:Trap #.ErrorTrapFlag/0
:Trap #.ErrorTrapFlag/0
DoSomething
DoSomething
Line 181: Line 181:
EndTrap
EndTrap
</syntaxhighlight>
</syntaxhighlight>
If the flag is true, error trapping is active, if not, the <source lang=apl inline>DoSomeThing</syntaxhighlight> statement will fail if an error occurs. This makes is much easier to debug an application.
If the flag is true, error trapping is active, if not, the <syntaxhighlight lang=apl inline>DoSomeThing</syntaxhighlight> statement will fail if an error occurs. This makes is much easier to debug an application.


You might need a more sophisticated mechanism for this, because under some circumstances you want to switch off most but not all error trapping statements. For example, if you use a logging mechanism which is logging every user action for analyzing purposes, the code doing this may cause an interrupt itself, for example because the disk is full which holds the logging files. In such a case it might be inappropriate that the logging code breaks the application. Therefore, you might control this code with <source lang=apl inline>:Trap</syntaxhighlight>-statements.
You might need a more sophisticated mechanism for this, because under some circumstances you want to switch off most but not all error trapping statements. For example, if you use a logging mechanism which is logging every user action for analyzing purposes, the code doing this may cause an interrupt itself, for example because the disk is full which holds the logging files. In such a case it might be inappropriate that the logging code breaks the application. Therefore, you might control this code with <syntaxhighlight lang=apl inline>:Trap</syntaxhighlight>-statements.


In such a case it might be a good idea to control the behavior of the application on different levels for code which is really essential in terms of business logic, for example, and for code which is not essential.
In such a case it might be a good idea to control the behavior of the application on different levels for code which is really essential in terms of business logic, for example, and for code which is not essential.
Line 191: Line 191:
== Code ==
== Code ==


{{Collapse|The below workspace contains all the code needed to implement the ideas discussed above.|<source lang=apl>
{{Collapse|The below workspace contains all the code needed to implement the ideas discussed above.|<syntaxhighlight lang=apl>
(⎕IO ⎕ML ⎕WX)←1 1 3
(⎕IO ⎕ML ⎕WX)←1 1 3


Navigation menu