Error Handling
ilusm has three error primitives - try, err, and asr - plus optional chaining for safe nil access. No exception classes, no try/catch blocks.
err(msg) - raise an error
Call err(msg) to raise a runtime error with a string message. Execution stops at that point unless caught by try.
validate(x) =
if x < 0: err("value must be non-negative")
x * 2
validate(5) # 10
validate(-1) # raises "value must be non-negative"
try(f) - catch errors
try takes a zero-argument function and calls it. If the function raises, try catches the error and returns it. Always returns {val, err}:
- On success:
{val: result, err: nil} - On failure:
{val: nil, err: "error message"}
load() =
err("file not found")
r = try(load)
prn r.err # "file not found"
prn r.val # nil
Use a lambda to wrap code with arguments:
r = try(\() validate(-1))
if r.err:
prn $"error: {r.err}"
| prn $"result: {r.val}"
Check success with the err field:
use fs
r = try(\() fs.rd("config.json"))
if r.err:
prn "config not found, using defaults"
| cfg = jsn.dec(r.val)
Common pattern: early exit on error
use fs
use jsn
load_config(path) =
r = try(\() fs.rd(path))
if r.err: err($"cannot read config: {r.err}")
r2 = try(\() jsn.dec(r.val))
if r2.err: err($"invalid JSON in config: {r2.err}")
r2.val
cfg = load_config("config.json")
Common pattern: result with default
r = try(\() risky_operation())
value = if r.err: default_value | r.val
asr(cond) - assertions
asr(cond) raises if cond is falsy. Optionally pass a message as the second argument.
asr(1 == 1) # passes silently
asr(2 + 2 == 4, "math broken") # passes
asr(fls) # raises "assertion failed"
asr(nil, "expected value") # raises "expected value"
asr is primarily used in tests with the test module, but you can use it anywhere you want a hard invariant.
Optional chaining - safe nil access
Use ?. and ?[ to access fields or indices without crashing when a value is nil. Returns nil instead of raising.
response = net.get("https://api.example.com/user")
data = jsn.dec(response.txt)
# Safe - returns nil if any step is nil
email = data?.user?.contact?.email
# Safe index access
first_tag = data?.tags?[0]
# Combine with or for defaults
host = cfg?.db?.host or "localhost"
Error messages from modules
Most stdlib functions that can fail raise an error string you can catch with try:
use fs
r = try(\() fs.rd("missing.txt"))
prn r.err # e.g. "open missing.txt: no such file or directory"