Early preview: syntax, standard library APIs, and tooling may change.

Core DataPattern Matching

Pattern Matching

match handles explicit cases and the compiler checks exhaustiveness.

Variants

Match every variant by name, binding payloads where a case carries data.

union Download:
	Queued
	Running(Int)
	Done(String)
	Failed(String)


pure func describe(download: Download) -> String:
	match download:
		Queued:
			"queued"
		Running(percent):
			"running " + percent.to_string() + "%"
		Done(path):
			"saved to " + path
		Failed(message):
			"failed: " + message
-- describe(Queued) == "queued"
-- describe(Running(40)) == "running 40%"
-- describe(Done("build/app")) == "saved to build/app"
-- describe(Failed("timeout")) == "failed: timeout"

Literals

Match literal strings, numbers, and booleans directly. Use a named fallback when you still need the unmatched value.

pure func command_label(command: String) -> String:
	match command:
		"add":
			"add item"
		"list":
			"list items"
		"quit":
			"exit"
		other:
			"unknown command: " + other


pure func status_label(code: Int) -> String:
	match code:
		200:
			"ok"
		201:
			"created"
		400:
			"bad request"
		404:
			"not found"
		500:
			"server error"
		other:
			"http " + other.to_string()


pure func confirmed_label(confirmed: Bool) -> String:
	match confirmed:
		True:
			"confirmed"
		False:
			"not confirmed"
-- command_label("add") == "add item"
-- command_label("sync") == "unknown command: sync"
-- status_label(400) == "bad request"
-- status_label(418) == "http 418"
-- confirmed_label(False) == "not confirmed"

Tuples

Tuple patterns can match the shape of each position at the same time.

pure func summarize(result: (Result[Int, String], Option[String])) -> String:
	match result:
		(Ok(total), None):
			"ready: " + total.to_string()
		(Ok(total), Some(warning)):
			"ready: " + total.to_string() + " (" + warning + ")"
		(Err(message), None):
			"blocked: " + message
		(Err(message), Some(warning)):
			"blocked: " + message + " (" + warning + ")"
-- summarize((Ok(12), None)) == "ready: 12"
-- summarize((Ok(12), Some("cached"))) == "ready: 12 (cached)"
-- summarize((Err("bad input"), None)) == "blocked: bad input"
-- summarize((Err("bad input"), Some("retry"))) == "blocked: bad input (retry)"

List Patterns

List patterns include exact shapes, such as [] and [cmd, item], plus spread tails such as [command, ...rest].

pure func route(args: List[String]) -> String:
	match args:
		[]:
			"missing command"
		["list"]:
			"list"
		["add", item]:
			"add " + item
		["remove", item]:
			"remove " + item
		["move", source, target]:
			"move " + source + " to " + target
		[command, ...rest]:
			"unknown " + command + " with " + rest
				.length()
				.to_string() + " args"
-- route([]) == "missing command"
-- route(["list"]) == "list"
-- route(["add", "milk"]) == "add milk"
-- route(["move", "todo", "done"]) == "move todo to done"
-- route(["archive", "old", "now"]) == "unknown archive with 2 args"

Nested Patterns

Patterns compose, so tuples, Option, Result, literals, and variants can be matched in one expression.

pure func describe_lookup(result: Result[Option[Int], String]) -> String:
	match result:
		Ok(Some(score)):
			"score: " + score.to_string()
		Ok(None):
			"missing score"
		Err(message):
			"error: " + message


pure func describe_pair(pair: (Option[Int], Option[Int])) -> String:
	match pair:
		(Some(left), Some(right)):
			"both: " + (left + right).to_string()
		(Some(left), None):
			"left only: " + left.to_string()
		(None, Some(right)):
			"right only: " + right.to_string()
		(None, None):
			"neither"
-- describe_lookup(Ok(Some(91))) == "score: 91"
-- describe_lookup(Ok(None)) == "missing score"
-- describe_lookup(Err("bad row")) == "error: bad row"
-- describe_pair((Some(1), Some(2))) == "both: 3"
-- describe_pair((None, None)) == "neither"

Catch-all

_ matches a value without binding it. Use it when every remaining case really has the same behavior.

pure func access_level(role: String) -> String:
	match role:
		"admin":
			"full"
		"editor":
			"write"
		_:
			"read-only"
-- access_level("admin") == "full"
-- access_level("viewer") == "read-only"

Exhaustiveness

A match must cover every possible input shape.

enum TrafficLight:
	Red
	Yellow
	Green


pure func next_action(light: TrafficLight) -> String:
	match light:
		Red:
			"stop"
		Yellow:
			"slow"
		Green:
			"go"
-- next_action(Red) == "stop"
-- next_action(Yellow) == "slow"
-- next_action(Green) == "go"

Example

commands.brp
pure func route(args: List[String]) -> String:
	match args:
		["add", item]: "add " + item
		["list"]: "list"
		[cmd, ...rest]:
			"unknown command: " + cmd + " with " + rest.length().to_string() + " args"
		[]: "missing command"


func main(args: List[String]) -> Void:
	print(route(["add", "milk"])) -- prints: add milk
	print(route(["archive", "old", "now"])) -- prints: unknown command: archive with 2 args

Try it

terminal
blorp run commands.brp