Error Handling
Why not try/catch?
Section titled “Why not try/catch?”Try/catch is verbose. An AI agent generating try { ... } catch (e) { ... } pays tokens for boilerplate, and if it forgets the catch block, the error is silently swallowed. ilo makes errors part of the type system - you can’t forget to handle them.
Quick errors with ^
Section titled “Quick errors with ^”Any function can bail out early with ^:
div a:n b:n > n -- says it returns a number... = b 0 ^"divide by zero" -- ...but ^ can throw an error / a b -- otherwise divideilo 'div a:n b:n>n;=b 0 ^"divide by zero";/a b' div 10 2# → 5
ilo 'div a:n b:n>n;=b 0 ^"divide by zero";/a b' div 10 0# → ^divide by zero^ exits the function immediately with an error. But there’s a problem: the return type says > n (a number), so any code calling this function expects a number back - it has no way to catch or handle the error. The error just crashes through.
Recoverable errors with R
Section titled “Recoverable errors with R”If you want callers to catch and handle errors, use R as the return type. This wraps the result so the caller can inspect it:
R success-type error-typeRead it as: “Result - first type is what you get on success, second is what you get on error”.
For example, R n t breaks down as:
| Part | Meaning |
|---|---|
R | this is a Result |
n | success value is a number |
t | error value is text |
Common patterns:
| Type | Success | Error |
|---|---|---|
R n t | number | text message |
R t t | text | text message |
R _ t | nothing (side effect) | text message |
Inside the function, ~ marks the success path and ^ marks the error path:
div a:n b:n > R n t = b 0 ^"divide by zero" ~ / a bNow callers can match on the result with ?:
show a:n b:n > t r = div a b ? r {~v: str v; ^e: e}~v:- if success, bind the value tov^e:- if error, bind the error toe
ilo 'div a:n b:n>R n t;=b 0 ^"divide by zero";~/a bshow a:n b:n>t;r=div a b;?r{~v:str v;^e:e}' show 10 0# → divide by zeroAuto-unwrap with !
Section titled “Auto-unwrap with !”Matching every Result is tedious. ! auto-unwraps: success continues, error propagates up. The calling function must also return R:
calc a:n b:n > R n t v = div! a b ~ * v 2div! a b = call div, if error return it immediately, if ok bind the value. One character (!) replaces a whole try/catch block.
Optional type O
Section titled “Optional type O”O T means “maybe a value of type T, maybe nil”:
f > O n; nilg > O n; 42ilo 'f>O n;nil' f# → nil
ilo 'f>O n;42' f# → 42Nil-coalesce with ??
Section titled “Nil-coalesce with ??”?? provides a default when a value is nil:
f x:O n > n; x ?? 0ilo 'f x:O n>n;x??0' f 42# → 42
ilo 'f>O n;nil' f# → nil?? chains: a??b??99 returns the first non-nil value, or 99.
Compact error codes
Section titled “Compact error codes”When type verification fails, ilo returns compact error codes:
ILO-T004: type mismatch: expected n, got tOne token, not a paragraph. Agents correct faster with less context.