Conditions.jl

Common Lisp like condition system for Julia
Author bertschi
Popularity
12 Stars
Updated Last
6 Months Ago
Started In
October 2023

Conditions.jl

Common Lisp like condition system for Julia

Start with the docstring of @handler_case to see how this works ...

help?> @handler_case
  Convenience macro around handler_bind which works similar to try-catch, i.e., the first matching handler is run with the stack unwound and its result is returned.

  Examples
  ≡≡≡≡≡≡≡≡≡≡

  julia> @handler_case @signal(3.0) begin
             Integer => c -> (:integer, c)
             Number => c -> (:number, c)
             Any => c -> (:any, c)
         end
  (:number, 3.0)

  See also @signal, handler_bind as well as nonlocal and jump.

Features

  • @signal does not unwind the stack, i.e., in contrast to throw

  • Restarts allow to continue execution higher up in the stack

Thinks to try

Use interactive setup and drop into Infiltrator where a condition was signalled!

using Conditions

toggle_interactive(true)

h(z) = @signal z
g(y) = h(y + 2)
f(x) = g(2 * x)

f(3)
# Try @trace and @locals from the infiltration prompt! 

Restarts are the real deal and allow top-level code decide how to fix stuff higher up on the stack!

using Conditions

function nonneg(x::Number)
    if x >= 0
        x
    else
        @signal ArgumentError("Negative $x")
    end
end

function validate(x::Number)
    @restart_case nonneg(x) begin
        :fix_value => newval -> newval
    end
end

function mysum(xs::AbstractVector)
    @restart_case sum(validate, xs) begin
        :rough_guess => () -> 42
    end
end

# Restart functions
function fix_value(condition)
    fixme = find_restart(:fix_value)
    if !isnothing(fixme)
        fixval = 2
        println("Fixing $(condition.msg) into $fixval")
        invoke_restart(fixme, fixval)
    end
end

function rough_guess(condition)
    guess = find_restart(:rough_guess)
    if !isnothing(guess)
        println("Guessing total after $condition")
        invoke_restart(guess)
    end
end

xs = [1, 2, -1, 3, 4, -5, 0]

# Top-level handler decides what to do
handler_bind(Handler(ArgumentError, fix_value)) do
    mysum(xs)
end

handler_bind(Handler(ArgumentError, rough_guess)) do
    mysum(xs)
end