Control Flow

Risor has two control flow constructs: if/else and match. Both are expressions. They produce a value, so you can assign the result directly to a variable or pass it into a function.

If / Else Expressions

An if/else block evaluates to the last expression in the taken branch:

let status = if (x > 0) { "positive" } else { "non-positive" }

Braces are always required, even for single expressions. The else branch is optional when using if as a statement:

if (len(items) == 0) {
    "no items"
}

Branches can span multiple statements. The branch's value is the last expression evaluated:

let result = if (condition) {
    let temp = compute()
    temp * 2
} else {
    0
}

Else If

Chain multiple conditions with else if:

let label = if (x > 100) {
    "large"
} else if (x > 0) {
    "positive"
} else if (x == 0) {
    "zero"
} else {
    "negative"
}

Match Expressions

match compares a value against a series of patterns and evaluates the first matching branch:

let label = match x {
    1 => "one"
    2 => "two"
    _ => "other"
}

The _ wildcard matches any value and serves as the default case.

See the pattern matching page for full details on match expressions, guard clauses, and advanced patterns.

Pipe Expressions

The pipe operator |> feeds the left-hand result as the argument to the right-hand side, creating clean data-flow chains:

let double = x => x * 2
let addOne = x => x + 1
5 |> double |> addOne              // 11

Pipes pair naturally with named functions and methods:

[3, 1, 2] |> sorted |> reversed    // [3, 2, 1]

Pipes work well for multi-step collection processing:

let evens = range(20) |> filter(x => x % 2 == 0) |> list
evens.map(x => x * x)

Each step receives the output of the previous one. Read top to bottom and you know exactly what's happening.

No Loops

There are no for or while loops in Risor. All iteration happens through functional methods and recursion:

// Transform a list
[1, 2, 3].map(x => x * 2)
 
// Accumulate a result
[1, 2, 3].reduce(0, (acc, x) => acc + x)
 
// Side effect for each element
let total = 0
[1, 2, 3].each(x => { total += x })
 
// Recursive computation
function factorial(n) {
    return if (n <= 1) { 1 } else { n * factorial(n - 1) }
}

This removes a common source of off-by-one and mutation bugs.


Risor © 2026 Deep Noodle