Condition Handlers

Handling errors in Idio works differently to many programming languages. In those, when an exception is raised then the computation state is unwound to where the handler is and the handler is executed and control continues from after the exception handling block. Think try/except in Python.

In Idio, when a condition is raised the VM pauses the current computation with all of the extant computation intact and walks back through the execution state looking to see if any handlers have been established for this condition type (or an ancestor of this condition type).

If one is found the associated handler is called with the condition as an argument which can do a number of things:

  1. it can return a value in which case that value is used in place of the errant computation and the original computation continues as if the errant computation had returned whatever the handler has just returned

    This isn’t used by the Idio defined error handlers as the conditions are not normally something easily recoverable from. There is an example below of unwise intervention.

    It also requires that the handler have some intimate knowledge of the state of processing at the time of the error.

    Suppose you wrote an ^i/o-no-such-file-error handler around a call to open-file. The expectation of the calling code is to receive an open file handle and so, if you want to handle the missing file by “quickly creating and opening another file” in its stead to return the open file handle, you also need to return the correct type of file (input or output) and with the correct mode.

  2. it can raise the condition again with a view that a previously established handler can take care of the problem

    The state computation is unchanged, the outer handler can still return a value.

  3. it can raise a totally different condition

    The state computation is unchanged, the outer handler, handling a different condition, can still return a value to the original point of failure.

  4. it can emulate the execution stack unwinding (try/except) by calling trap-return in the handler


You can establish handlers in two ways:

  1. trap establishes a handler for a condition type (or types) around a block of code

  2. set-default-handler! establishes a handler for a condition type

    In that sense, set-default-handler! is much more like the shell’s trap builtin.

If you don’t do anything you will find there are several handlers established by default, including:

Example Handler

Suppose we want to handle ^rt-divide-by-zero-error:

trap ^rt-divide-by-zero-error (function (c) {
                                 ; we could generate a scathing report with
                                 ; condition-report "fool!" c

                                 ; return a value indicating the
                                 ; user's foolishness
                                 'fool
}) {
  1 / 0
}

Hmm, nothing. Well, technically, trap itself will have returned the symbol fool.

Suppose the body was more complex and went on to use the returned value:

trap ^rt-divide-by-zero-error (function (c) {
                                 'fool
}) {
  t := 1 / 0
  1 + t
}

This shows our handler as being incredibly naïve as now we get an ^rt-parameter-type-error in the next expression as the addition, +, won’t accept the symbol as a valid type.

We can revert to the more common try/expect behaviour by returning from the trap itself with trap-return:

trap ^rt-divide-by-zero-error (function (c) {
                                 trap-return 'fool
}) {
  t := 1 / 0
  1 + t
}

Here, we return the symbol fool from trap as soon as the divide-by-zero error occurs and without stumbling into the problem with addition.

Handler Functions

template trap conditions handler body

Establish a handler handler for conditions conditions around body

Param conditions:

a condition type or a list of condition types

Type conditions:

symbol or a list of symbols

Param handler:

the handler

Type handler:

1-ary function

Param body:

the body

Type body:

expression

trap may not return.

Normally, trap will return whatever body returns – usually body is a block and so the value of the last expression evaluated.

If a condition is raised during the processing of body then trap may (eventually) continue processing body if a handler returns a value in place of the condition-raising expression. trap will therefore return the value of the last expression evaluated, as normal.

The condition handler may be restart-condition-handler in which case the current top-level expression is discarded and its continuation is run.

The condition handler may be reset-condition-handler in which case the idio process will attempt to exit.

template trap-return [v]

return v to the continuation of trap

Param v:

the value to return, defaults to #<void>

Type v:

any, optional

template suppress-errors! conditions body

Establish a handler (that returns #<void>) for conditions conditions around body

Param conditions:

a condition type or a list of condition types

Type conditions:

a condition type or a list of condition types

Param body:

the body expression

Type body:

expression

Attention

suppress-errors! has a very narrow use case. In general, returning a value to failed computations requires intimate knowledge of the computation.

However, suppress-errors! is used exclusively with ^system-error in situations where there is a race condition between the parent and child after a libc/fork which results in one or the other failing.

Standard Handlers

function default-SIGCHLD-handler c

The default handler for an ^rt-signal-SIGCHLD condition

This invokes do-job-notification.

Param c:

the condition

Type c:

condition instance

function default-racse-handler c

The default handler for an ^rt-async-command-status-error condition

Param c:

the condition

Type c:

condition instance

Return:

#<unspec>

The default behaviour is to report but otherwise ignore failed asynchronous processes

See also

suppress-async-command-report! for means to change the default behaviour.

function default-rcse-handler c

The default handler for an ^rt-command-status-error condition

Param c:

the condition

Type c:

condition instance

Return:

see below

If the command exits with a non-zero status (from exit(3) or by signal) then we exit the same way.

Otherwise #<unspec>

See also

suppress-exit-on-error! and suppress-pipefail! for means to change the default behaviour.

function default-condition-handler c

Invoke the default handler for condition c

If there is no default handler: - if the session is interactive then the debugger is invoked - otherwise the condition is re-raised

Param c:

the condition

Type c:

condition instance

does not return per se

function set-default-handler! ct handler

set the default handler for condition type ct to handler

If a condition of type ct is not otherwise handled then handler will be invoked with the continuation.

Param ct:

condition type

Type ct:

condition type

Param handler:

handler for the condition type

Type handler:

function

Return:

#<unspec>

function clear-default-handler! ct

unset the default handler for condition type ct

The default behaviour for conditions of type ct will resume.

Param ct:

condition type

Type ct:

condition type

Return:

#<unspec>

function restart-condition-handler c

Restart the VM with the continuation of the current top-level expression.

Param c:

the condition

Type c:

condition instance

does not return per se

Warning

restart-condition-handler is only effective if the session is interactive

function reset-condition-handler c

Stop the VM and exit non-zero.

Param c:

the condition

Type c:

condition instance

Does not return.

Last built at 2024-05-17T06:10:45Z+0000 from 62cca4c (dev) for Idio 0.3.b.6