?=
?= keeps fallible steps flat, avoiding the pyramid of doom from nested match blocks. It works only with Option and Result: Some(value) and Ok(value) bind the value; None and Err(error) return early from the enclosing function.
Option Binding
Inside a function returning Option, ?= unwraps Some(value) or returns None immediately.
pure func first_display_name(names: List[String]) -> Option[String]:
name ?= names.get(0)
Some(name.capitalize())
Result Binding
Inside a function returning Result, ?= unwraps Ok(value) or returns the Err(error) immediately.
pure func sum_two(left_raw: String, right_raw: String) -> Result[Int, String]:
left ?= left_raw
.parse_int()
.to_result("invalid left")
right ?= right_raw
.parse_int()
.to_result("invalid right")
Ok(left + right)
Without ?=
When each step can fail, explicit match handling pushes the success path inward.
pure func sum_two(left_raw: String, right_raw: String) -> Result[Int, String]:
match (
left_raw
.parse_int()
.to_result("invalid left")
):
Ok(left):
match (
right_raw
.parse_int()
.to_result("invalid right")
):
Ok(right):
Ok(left + right)
Err(msg):
Err(msg)
Err(msg):
Err(msg)
Loop Restriction
Direct ?= is rejected inside loop bodies. Bind before the loop, handle each failure with match, or put local failure handling in a function.
pure func parse_scores(raw_scores: List[String]) -> Option[List[Int]]:
var scores: List[Int] = []
for raw in raw_scores:
score ?= raw.parse_int()
scores = scores.append(score)
Some(scores)
Lambda Boundaries
A lambda starts a fresh function boundary, even inside a loop. It must return Option to bind an Option, or Result to bind a Result. Here None returns from the lambda, not the outer function.
func count_valid(raw_scores: List[String]) -> Int:
var count: Int = 0
for raw in raw_scores:
parse_score = func(text: String) -> Option[Int]:
score ?= text.parse_int()
Some(score)
if parse_score(raw).is_some():
count += 1
count
func main(args: List[String]) -> Void:
print(count_valid(["91", "invalid", "100"])) -- prints: 2
Example
This version uses ?= to keep the success path linear while preserving the same error behavior.
pure func parse_named_int(name: String, raw: String) -> Result[Int, String]:
raw
.parse_int()
.to_result("invalid " + name)
pure func sum_two(left_raw: String, right_raw: String) -> Result[Int, String]:
left ?= parse_named_int("left", left_raw)
right ?= parse_named_int("right", right_raw)
Ok(left + right)
func main(args: List[String]) -> Void:
match sum_two("20", "22"):
Ok(total): print(total) -- prints: 42
Err(msg): print(msg)
Try it
blorp run parse-sum.brp