Data & Control

Patterns & Control Flow

Choose if, match, or for, trace branch results, and extract values with patterns.

Choose if to test a condition, match to select a case, and for to visit each item. Trace the example to see which task counts toward the remaining time.

Install Topaz using First Program. The Data and Control lesson introduces the records and array used here.

Classify tasks and total the remaining time

Save this code as data-control.tpz in your working folder.

TOPAZ
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")

In that folder, check the file and run it.

BASH
topaz check data-control.tpz
topaz run data-control.tpz

The check succeeds with exit code 0. The program prints these results.

Output
Run first program: done
Build application: focused
remaining: 25 minutes

Follow each decision

The loop gives each array element the name task. For the completed task, !task.done is false, so remaining stays 0. The function pace returns "done" before reaching its match.

The second task is unfinished. Its 25 minutes are added to remaining. In pace, 25 matches 16..30, so the result is "focused".

match evaluates task.minutes once and tests arms from top to bottom. Here, each case starts an arm with a pattern and a result. The range patterns test which interval contains the value. _ matches any remaining value, so other integers produce "deep".

TaskChooseResult
Select by a boolean conditionifThe selected branch runs
Select by a value or its structurematchThe selected arm supplies its value
Process each item in orderforEach iteration runs the body

Use if condition { ... } else { ... } when both outcomes need handling. To use an if or match as a value, give its branches compatible result types. A for used as an expression collects body results in an Array. A statement-position for or a while loop performs actions without producing a result value.

Use patterns to test and name values

A pattern can check a value, give it a name, or extract its parts. A literal or range pattern tests the value. A name such as task binds the value without testing its contents.

Some(value) extracts a present value from Option. Err(message) extracts an error from Result. Record patterns extract the fields they name. See Records & Nominal Data for record and enum declarations.

The array pattern [head, ..tail] binds the first item to head and the remaining items to the array tail. _ matches a value without giving it a name.

Use if let pattern = value { ... } to run a block when one pattern matches. Add else if a mismatch needs handling. while let repeats until the pattern fails. The names they bind are immutable and visible inside the successful block.

Correct a pattern

In an array pattern, change [head, ...tail] to [head, ..tail]. Three dots expand arrays in literals or calls. Two dots collect the remaining items in a pattern.

For an enum, create a value with the enum name, as in Status.Paused("review"). In a match arm, use the variant name, as in case Paused(reason) =>.

Match coverage and iteration rules

  • Enum matches must handle every declared variant. A guard adds a condition to an arm, so a guarded arm does not establish coverage.
  • When static checking cannot establish complete coverage, include an explicit _ arm for the remaining cases.
  • Patterns evaluate from left to right. Names bound by a failed alternative are unavailable in later alternatives.
  • A pattern mismatch in a comprehension causes a runtime fault. Use an if filter before a later pattern extracts parts from selected items.
  • Arrays, integer ranges, sets, and map.keys have defined iteration orders. Strings require scalar operations before iteration.
  • User-defined patterns and iteration, generators, exception handling, and labels on while or for are not supported.

Continue to Collections & Comprehensions to choose a collection and filter its values.