Conditions & Restarts
Atomo uses a condition system very similar to Common Lisp's.
Signals
signal: v → @ok
Sends a value to all bound handlers, nearest-first.
Handlers
action bind: handlers → any | action is-a?: Block | handlers is-a?: Block
Performs action, with a handler for all signals matched in handlers block. Returns the result of action.
The contents of handlers should be Association expressions, e.g. pattern -> responder, where pattern is a match on the signal value, and responder is either a Block (which will be called with the signal value itself as an argument) or an expression to be evaluated in response to the signal.
Example:
> { signal: @foo } bind: { @foo -> "fooed!" print } fooed! @ok > { signal: @(bar: 42) } bind: { @(bar: x) -> @(fooed: x) print } @(fooed: 42) @ok > { error: @uh-oh } bind: { Error -> { e | @(got-error: e) print } } @(got-error: <error @uh-oh>) ERROR: <error @uh-oh>
A responder may often invoke restart: or restart:with:, which is the only way they can affect the return value of action.
After a handler responds to a signal, the signal continues outward to the next recently bound handler:
action with-restarts: restarts → any | action is-a?: Block | restarts is-a?: Block
Call action, binding the restarts described by restarts. These restarts, when invoked, will replace the result of action.
The contents of restarts should be Association expressions, e.g. name -> expression, where name is the name for the restart, and expression is the action to be performed when the restart is invoked.
Example:
> { { x } with-restarts: { ignore -> @ok } } bind: { Error -> restart: 'ignore } @ok
action with-restarts: restarts bind: handlers → any | action is-a?: Block | restarts is-a?: Block | handlers is-a?: Block
Trivial macro which just expands to with-restarts: and bind:.
restart: name with: (... args) → any | name is-a?: Expression
Invokes restart name with args passed to the block.
Traditional Exceptions
These methods are specific to errors, and provide a system similar to that of traditional exception handling. They are defined in terms of the more general condition system.
action catch: recover → any | action is-a?: Block | recover responds-to?: @call:
Execute action call, catching any errors and passing them to recover for handling.
Yields the result of action or recover.
action catch: recover ensuring: cleanup → any | [action, recover, cleanup] all?: @(is-a?: Block)
Execute action call, catching any errors and passing them to recover for handling, and ensuring that cleanup is called after the action is executed.
Yields the result of action or recover.
action handle: branches → any | action is-a?: Block | branches is-a?: Block
Similar to catch:, but branches is a block containing exception patterns bound to the action to take if they're thrown. If none of the exceptions match any of the patterns, the exception is re-thrown.
action ensuring: cleanup → any | action is-a?: Block | cleanup is-a?: Block
Execute action call, ensuring that cleanup is called after the action is executed.
Yields the result of action.