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 union or enum 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", from, to]: "move " + from + " to " + to
		[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