Use if for a single yes-or-no condition, match when the shape or category of a value determines the result, and for when performing the same work on each item. Patterns are constructs inside match and for that test data and bind names to required parts.
Run one example
Save this as data-control.tpz:
record StudyTask {
title: string,
minutes: int,
done: bool = false,
}
function pace(task: StudyTask) -> string {
if task.done {
return "done"
}
match task.minutes {
case 1..15 => "quick"
case 16..30 => "focused"
case _ => "deep"
}
}
let tasks: Array<StudyTask> = [
StudyTask { title: "Run first program", minutes: 10, done: true },
StudyTask { title: "Build application", minutes: 25 },
]
let mut remaining = 0
for task in tasks {
if !task.done {
remaining = remaining + task.minutes
}
print("{task.title}: {pace(task)}")
}
print("remaining: {remaining} minutes")Check it before running it:
topaz check data-control.tpz
topaz run data-control.tpzThe output follows array order:
Run first program: done
Build application: focused
remaining: 25 minutesFollow the decisions
The loop binds each array element to task. The inner if evaluates a single boolean condition to check whether the task is unfinished. It neither classifies multiple cases nor destructures the value.
When both outcomes require handling, write if condition { ... } else { ... }.
Compatible branch values allow the entire if expression to yield a value.
pace first uses if for the completed task case. Its match expression then evaluates task.minutes once and tests the case arms from top to bottom. 1..15 and 16..30 are range patterns. _ serves as the catch-all pattern, ensuring every other integer produces a result.
| Choose | When it fits | What it produces |
|---|---|---|
if | A single condition selects between branches | A value when compatible branches yield values, or an effect-only statement otherwise |
match | A value has several meaningful shapes or categories | The selected arm's value, along with any pattern bindings in that arm |
for | Every item must be visited in iteration order | Statements executing side effects, or an Array of body results when used as an expression |
Patterns select and bind
A literal or range pattern tests a value. An identifier such as task binds the matched value. Constructor patterns such as Some(value) and Err(message) destructure Option and Result values. Record patterns extract named fields.
Array patterns use .. rather than call spread syntax. case [head, ..tail] => binds the first element and assigns the remaining elements to tail as an Array. Use _ when a fallback is required but the value is not needed.
if let pattern = value { ... } is useful for matching a single successful shape with an optional fallback. while let repeats the test until pattern matching fails. Their bindings are immutable and scoped exclusively to the successful block.
Common mistake
Do not write [head, ...tail] inside a pattern. The ... syntax is reserved for array and call spreads. The standard pattern for capturing remaining elements is [head, ..tail].
Enum construction and enum pattern matching are intentionally distinct. Construct variants using the qualified enum name, such as Status.Paused("review"), but match variants without qualification, such as case Paused(reason) =>.
Exact behavior that affects a choice
- A value-producing
iformatchrequires compatible branch results. - A closed enum match is verified for complete variant coverage. A guarded arm does not count toward that coverage.
- When the checker cannot verify that a match is complete, use an explicit
_arm. - Patterns evaluate from left to right. Bindings from a failed alternative do not leak into subsequent alternatives.
- A refutable pattern failure inside a collection comprehension produces an error rather than filtering items. Add an explicit comprehension
ifclause to skip items. Array, integer ranges,Set, andmap.keyshave a defined iteration order. Strings are not directly iterable.whileloops and statement-positionforloops produce no value. Aforloop evaluated in expression position collects its body results in source order.
User-defined patterns, generators, user-defined iteration, exception-handling control structures, and labels on while or for loops are not supported in the current language.
Continue from the course example in Data and Control. See Records & Nominal Data for the shapes patterns can destructure, and Collections & Comprehensions for iteration and filtering.