Condition System

Rather than traditional exceptions, Atomy sports a condition/restart system modeled on Common Lisp's design. The native Ruby exception handling is available, but conditions and restarts are much more flexible.

Conditions

Condition:
  Error(@backtrace):
    SimpleError(@value)
    ExceptionError(@exception)
    NoRestartError(@name)
    PortError(@port):
      EndOfFile
  Warning(@backtrace):
    SimpleWarning(@value)

Condition system hierarchy. You should subclass one of these to create your own conditions.

Condition name => String

Get the name of a condition. By default, this will be the class name, but you may override this for your own behaviour.

Condition message => String

A human-friendly message displayed for the condition. Override this.

Handling

restart(name, *args) => _
  | name is-a(Symbol)?

Invoke the name restart, passing args along to its callback.

See with-restarts.

Example:

> { with-restarts(foo -> 42): signal(.bar) } bind: .bar -> restart(.foo)
42
{ ~*body } bind: ~*handlers

Register handlers for various signals for the duration of body's execution.

The result is the result of body.

Example:

> { signal(.a) } bind: .a -> puts("got A!")
got A!
nil
> { signal(.b) } bind: .a -> puts("got A!")
nil
> { { signal(.a) } bind: .a -> puts("x") } bind: .a -> puts("y")
x
y
nil
with-restarts(~*restarts): ~*body

Register restarts available for the duration of body's execution.

The restarts should be in the form of name(*args) -> expr.

The result is the result of body.

Example:

> { with-restarts(x -> 1, y -> 2): signal(.a) } bind: .a -> restart(.x)
1
> { with-restarts(x -> 1, y -> 2): signal(.a) } bind: .a -> restart(.y)
2
> { with-restarts(x(a) -> a * 7): signal(.a) } bind: .a -> restart(.x, 6)
42

Signalling

signal(c) => any

Signal a value through all bound handlers, nearest-first, stopping when a restart is invoked.

Example:

> signal(.foo)
nil
> { signal(.foo) } bind: .foo -> puts("got foo")
got foo
nil

error(x) => _

Like signal, except that if no restart is invoked, the current ^debugger is started.

If the given value is not an Error, it is wrapped in a SimpleError. If the value is a Ruby Exception, it is wrapped in an ExceptionError.

Example:

> error("Oh no!")
condition.ay::SimpleError: Oh no!
> { error("Oh no!") } bind: Error -> puts("INCOMING")
INCOMING
condition.ay::SimpleError: Oh no!

warning(x) => nil

Like signal, except that if no restart is invoked, the warning is printed to ^error-port.

If the given value is not a Warning, it is wrapped in a SimpleWarning. Warning messages can be muffled by binding for Warning and invoking the .muffle-warning restart.

Example:

> warning("Suspicious!")
condition.ay::SimpleWarning: Suspicious!
nil
> { warning("Quiet, you!") } bind: Warning -> restart(.muffle-warning)
nil

Debuggers

The default debugger. This will show the condition name, its message, and let the user pick from the available restarts.

DefaultDebugger run(condition) => _

Show the condition message and list the current available restarts, asking which one to invoke.

The current debugger. run will be called with the condition as an argument.