Skip to main content

Errors & Debugging

Aborting​

abort(message) stops the program with an error and a stack trace:

fn first(l: Empty) { abort("first: seq is empty") }

Failed pattern matches – an assignment that doesn't fit, a match with no matching clause, a call with no matching method – abort in the same way.

Optionals and results​

For errors you expect to handle, Raven leans on ordinary values rather than exceptions. The standard library defines two small bundles:

bundle Optional { Some(x), Nil() }
bundle Result { Ok(x), Err(e) }

nil is shorthand for Nil(), with nil? and notnil to test and unwrap it – this is what the iteration protocol uses to signal exhaustion. unwrap takes the value out of an Ok, aborting on an Err. Pattern matching handles both cleanly:

match parse(input) {
let Ok(x) { use(x) }
let Err(e) { println(e) }
}

There is no try/catch; failures either abort, or are values you match on.

show and test​

The quickest debugging tools are macros. show prints an expression together with its value, and test checks a condition:

> show 2+2
(2 + 2) = 4
> test 2+2 == 4
pass: ((2 + 2) == 4)

Sprinkling test lines through a file is the current idiom for writing tests – run the file and grep for fail.

Stack traces and the debugger​

Compiled programs carry debug metadata (DWARF), so aborts come with reasonable stack traces pointing at your .rv source, and you can step through Raven code in a debugger – Chrome DevTools for wasm in the browser, or VS Code's debugger via the editor extension. Pass --strip to raven build to remove the metadata from release binaries.

Compiler development flags​

Two flags to know when hunting odd behaviour: --no-inline disables function inlining, and --no-memcheck disables the allocation checks that verify reference counts at exit. Mostly these are for debugging the compiler itself, but they can help isolate a miscompilation – see Contributing.