Pattern matching
Turn values into categories, handle missing data, and dispatch different actions clearly.
Pattern matching compares one value against a set of ordered cases. It is especially useful for turning continuous market values into named regimes, choosing a visual style, handling missing data, or acting on an enum value.
The first matching arm wins, so write the most specific cases first.
Produce a value
Use a match expression when each case should produce a value:
fn describeMove(changePct: float?) -> string {
return match changePct {
null => "Waiting for data"
>= 2.0 => "Strong rise"
> 0.0 => "Rise"
<= -2.0 => "Strong fall"
< 0.0 => "Fall"
_ => "Flat"
}
}Relational patterns compare the subject with the value after <, <=, >, or >=. Because arms are checked from top to bottom, >= 2.0 must come before > 0.0.
The final _ arm matches anything not handled above it. Match expressions must cover every possible value, so a wildcard is the simplest safe ending when the cases are not a small fixed set.
Choose a color
A match expression can return any single compatible type, including colors:
let shade = match imbalance {
null => color.gray
>= 0.6 => color.green
<= -0.6 => color.red
_ => color.yellow
}This is often easier to extend than a chain of nested conditional expressions.
Run different actions
Use a match statement when the arms should update state or perform an action rather than produce a value:
state risingBars = 0
on chart.update {
let move = chart.close - chart.open
match move {
null => {
risingBars = 0
}
> 0.0 => {
risingBars += 1
}
_ => {
risingBars = 0
}
}
}A match statement does not have to cover every possible value. If no arm matches, it simply performs no action. Add _ when you do want a default action.
For a single assignment or function call, braces are optional:
match move {
> 0.0 => risingBars += 1
_ => risingBars = 0
}Use braces when an arm needs more than one statement.
Combine cases
Separate alternatives with | when several values should share one result:
fn sessionName(hour: int) -> string {
return match hour {
0 | 1 | 2 | 3 | 4 | 5 => "Overnight"
6 | 7 | 8 | 9 | 10 | 11 => "Morning"
12 | 13 | 14 | 15 | 16 | 17 => "Afternoon"
_ => "Evening"
}
}Alternatives can also mix exact and relational cases:
let label = match score {
null => "Unavailable"
0.0 | < -3.0 => "Inactive"
> 3.0 => "High"
_ => "Normal"
}Handle missing values first
For a nullable subject, put null in its own arm:
let distance = match previousHigh {
null => 0.0
_ => (chart.close ?? previousHigh) - previousHigh
}After the null arm, later arms can use previousHigh as a present value. This avoids scattering ?? fallbacks through the calculation.
An exact-value or relational pattern never matches null; only null and _ do.
Match enum values
Matching is a clear way to convert a fixed option into behaviour:
fn unitLabel(unit: BookUnit) -> string {
return match unit {
bookunit.base => "Base quantity"
bookunit.quote => "Quote value"
}
}When every enum value is listed, no wildcard is needed. Keeping the cases explicit also means a newly added enum option can be identified instead of silently falling into an old default.
Match against a calculated value
An exact pattern can be a variable or calculation, not only a literal:
let hour = time.hour(barTime)
let phase = match hour {
openHour => "Session open"
> openHour => "After the open"
_ => "Before the open"
}The match subject is calculated once. Arms are then considered in order until one matches.
Exact patterns use plain equality, with no tolerance, so they suit integers, strings, and enums. Two calculated floats are rarely exactly equal, so compare a price with a level using relational arms:
let referenceLevel = previousClose ?? 0.0
let location = match price {
> referenceLevel => "Above previous close"
< referenceLevel => "Below previous close"
_ => "At previous close"
}What patterns do not do
Patterns compare a single subject. They do not bind a new variable, destructure a user-defined type, or add an if guard to an arm.
Read fields before matching when working with a user-defined value:
let strength = signal.strength
let tier = match strength {
>= 0.8 => "Strong"
>= 0.5 => "Moderate"
_ => "Weak"
}For an extra condition, either calculate it before the match or use an if inside a statement arm.
Match or if?
Use match when several outcomes all depend on the same subject. Use if when each branch tests a substantially different condition.
A good rule of thumb:
- Use a match expression to calculate one result from several cases.
- Use a match statement to dispatch several actions from one value.
- Use
iffor one or two boolean decisions or unrelated conditions.