Skip to content
Playground

Control Flow

Control-flow forms are expressions unless noted otherwise.

if

yulang
if x > 0: "positive" else: "non-positive"

if cond:
    a
else:
    b

if cond { a } else { b }

if expects a bool condition. An if without else is statement-like: the then-branch is evaluated for its effects, its value is discarded, and the whole expression returns ().

elsif

elsif adds another condition between if and else. Conditions are tried from left to right; the first true branch wins, and else handles the case where every condition is false.

yulang
my size n =
    if n < 0: "negative"
    elsif n == 0: "zero"
    elsif n < 10: "small"
    else: "large"

say (size 7)

case

yulang
case value:
    0 -> "zero"
    n if n < 0 -> "negative"
    _ -> "other"

case arms are tried top-to-bottom. Guards use if after the pattern.

Guards

A case guard follows the pattern with if or where. The guard can use names bound by the pattern. It runs only after that pattern matches; when it returns false, matching continues with the next arm.

yulang
my sign n = case n:
    value if value < 0 -> "negative"
    value if value == 0 -> "zero"
    _ -> "positive"

say (sign 3)

catch

yulang
act console:
    our read: () -> str

catch console::read():
    console::read(), k -> k "42"
    value -> value

An operation arm receives the operation payload and a continuation k. Calling k value resumes the computation. A value arm handles normal completion.

Guards

A catch guard follows the operation pattern and continuation with if or where. A false guard leaves that arm unselected and tries the next matching arm.

yulang
act signal:
    our ask: () -> int

my result = catch signal::ask():
    signal::ask(), k if false -> 0
    signal::ask(), k -> k 42
    value -> value

say result

Labels on case and catch

case 'label value: and catch 'label action: give the entire arm set a recursive name. The label belongs to the arm set, not to an individual arm. Calling 'label next from a body applies the same case or catch arms to next.

yulang
my result = case 'count 3:
    0 -> "done"
    n -> 'count (n - 1)

say result

The same spelling re-enters a labelled catch:

yulang
my result = catch 'again 3:
    n if n > 0 -> 'again (n - 1)
    n -> n

say result

for

yulang
for x in 0..10:        // 11 iterations: 0..10 is inclusive (0..<10 is half-open)
    say x

for x in xs: iterates over a value implementing Fold. The body is lowered to a function, and a plain for expression returns ().

Loop control comes from the prelude:

yulang
for x in 0..:
    if x == 10: last

last, next, and redo break, skip, or restart the current iteration.

Labels

yulang
for 'outer x in 0..:
    for y in 0..:
        if y == 3: last 'outer

Labelled loops pass a label value to the body. Prefix forms such as last 'outer, next 'outer, and redo 'outer target that labelled loop.

sub and return

yulang
sub:
    for x in 0..:
        if x == 5: return x
    0

sub: creates an early-return scope. return value exits the innermost sub:. The nullfix form return returns ().

Labelled sub works similarly:

yulang
sub 'done:
    'done.return 42
    0

sub, return, last, next, and redo are standard-library/prelude surface forms, not parser-only keywords.

Blocks and Lambdas

yulang
{
    my x = 1
    x + 1
}

my inc = \x -> x + 1
my add = \x y -> x + y

(inc 1, add 1 2)

Blocks evaluate statements in order and return the final expression. Lambdas use \ and may take multiple curried arguments.

Yulang