Control flow and pattern matching
Conditionals, loops, and match, all in prefix form.
Like every NURL expression, control flow uses prefix form: the keyword comes first, then its operands.
Conditionals: ?
? cond then elseBoth then and else are full expressions. A block { ... } is the usual
form. Both branches must give the same type.
? > x 0 { ( puts `positive\n` ) } { ( puts `non-positive\n` ) }Chained conditionals read top to bottom. Each else slot holds the next
?:
? div3
{ ( nurl_print `Fizz\n` ) }
? div5
{ ( nurl_print `Buzz\n` ) }
{ ( nurl_print_int i ) }Count the operands
? takes exactly three operands. It is easy to get a chain of & a b c d
inside a condition wrong. See Binary operators below.
Loops
While
~ cond { body }: ~ i i 1
~ <= i 10 {
( nurl_print_int i )
= i + i 1
}Foreach
~ item collection { body }collection must be a slice. The element binding is a borrow. It is not
owned, the runtime does not drop it at the end of the iteration, and you
cannot reassign it.
: [i nums [i | 1 2 3 4 5]
~ n nums {
( puts ( nurl_str_int n ) )
}The loop body may not mutate collection itself. The borrow checker rejects
push, remove, clear, and similar calls on it. See
Memory and ownership.
Binary operators
Arithmetic, comparison, shift, and logical operators are all strictly binary — they take exactly two operands. For a chain of more than two, write one operator per pair, n−1 operators for n values:
& a & b c // a && b && c
| | a b c d // a || b || c || dThe compiler warns on the common mistake of writing an n-ary chain as flat
arguments to ?. The extra values silently become the ?'s then/else
operands. They do not become more operands to &.
Match: ??
?? expr { arm* }Matches an enum value, an ?T option, or a !T E result. You must cover
every variant. Use an explicit arm or a _ wildcard. The compiler
checks this at compile time.
?? val {
JNull → `null`
JNum n → ( nurl_str_int n )
KeyPress c m → ( key_event c m ) // binds two payload values
Ok 200 → `ok` // matches only when payload == 200
_ → `other`
}A pattern can be a variant name, _ for wildcard, or A | B (an or-pattern
that covers several variants with no payload binding). A payload slot can be
an identifier that binds the value, or an integer literal that must match
exactly.
An arm may add a guard — ? condition between the payload and →. The
runtime checks the guard after it binds the payload. A guarded arm does not
by itself satisfy exhaustiveness for its variant. You still need a plain arm
or _ to cover the case where the guard is false.
?? msg {
Move n ? > n 0 → `forward`
Move n → `back-or-stay`
_ → `other`
}?T and !T E are ordinary enums, so they match the
same way. Use T / F as the tag pattern for ?T:
?? maybe_n {
T n → ( nurl_print_int n )
F → ( nurl_print `nothing\n` )
}See Error handling for the full picture, including
the \ try-propagate shortcut.
Channel select
A ?? immediately followed by { (no scrutinee expression) starts a
different construct — a Go-style channel select. It runs the body of
whichever channel becomes ready first. See
Concurrency.
Next
Last updated on