Part IV — Patterns & errors

Matching values with case/~, destructuring with .bind:, and raising and catching errors.

Nav: Foundations · Blocks & control · Objects · Patterns & errors · Concurrency & iteration · Networking & the web · Types · Tooling · Library & reference · Appendices


14. Pattern matching & case

Rules

var score = 87
var grade = score.case:{
    .when:(90..101) do:'A';                "* range membership
    .when:(80..90)  do:'B';
    .when:{ |n| n < 0 } do:'invalid';      "* predicate block, gets the subject
    .default:'F'
}
grade;                                     "* -> 'B'
var name = 'Ada'
name.case:{
    .when:#/^[A-Z]+$/ do:{ 'shouting'.print };   "* regex match
    .when:'Ada'       do:{ 'hi Ada'.print };     "* equality
    .default:{ 'unknown'.print }
}

The same ~ operator works standalone, with the matcher on the left: (1..10) ~ 5, #/b/ ~ 'abc', TypeError ~ value, { |n| n > 0 } ~ x.

Destructuring into blocks — the bind: family

Rules

#(3 4).bind:{ |w h| w * h }                       "* -> 12
#{'w': 5 'h': 4}.bind:{ |w h| w * h }             "* -> 20
('1/2/3'.split:'/').bind:{ |a b c| a + c }        "* -> '13'

var m = #/(?<user>\w+)@(?<host>[\w.]+)/.match:'ada@example.org'
m.bind:{ |host user| %'%{host} gets mail for %{user}' }   "* -> 'example.org gets mail for ada'
m.at:'user';                                      "* -> 'ada'
m.at:2;                                           "* -> 'example.org'
(#/(\d+)-(\d+)/.match:'10-20').bind:{ |lo hi| hi.to_integer - lo.to_integer }   "* -> 10
#/x/.match:'abc'                                  "* -> nil

15. Errors & stack traces

Rules

var amount = -5
var result = {
    (amount < 0).if:{ ArgumentError.throw:'amount must be >= 0' };
    .process:amount                "* reached only when the check passes
}.catch:{ |e:ArgumentError| ('bad input: ' + e.message).print; 0 }
 catch:{ |e:IoError|        ('io failed: ' + e.message).print; -1 }
 finally:{ 'done'.print }
"* anything that isn't an ArgumentError or IoError re-raises automatically —
"* most-specific handler first, no explicit re-throw needed.
result                             "* -> 0

Internal failures surface as the matching Quoin error type — e.g. an out-of-range index or a type mismatch becomes a catchable TypeError/IndexError, and sending an unknown selector becomes a MessageNotUnderstood — each with a message you can read.

Placeholder statements

Three statement-only markers hold a place for code that isn't there yet — the todo!() family of Quoin. They are statements, not expressions (var x = ... is a parse error):

{ ... }.catch:{ |e:NotImplementedError| e.message }    "* -> not implemented
{ !!! }.catch:{ |e:UnreachableError| e.message }       "* -> reached unreachable code

Both throwing forms are ordinary Error subclasses: a plain catch:{ |e:Error| … } catches them, traces point at the placeholder, and a test can pin one with .does:{ ... } throw:NotImplementedError.

Stack traces: uncaught errors print a highlighted trace (with source snippets). The mechanics are an implementation detail; nothing in the language surface depends on them.


Next: Part V — Concurrency & iteration.