Pattern Matching
Table of contents
- Your first match expression
- The structure
- Literal patterns
- Wildcard pattern: _
- Variable patterns
- Option patterns
- Exhaustiveness checking
- Practical examples
- Matching with complex expressions
- Combining with where bindings
- Real-world examples
- Pattern matching vs if-then-else
- When to use pattern matching
- Try it yourself!
You’ve already seen pattern matching with Options. But match is much more powerful! It lets you handle different cases elegantly and safely, replacing complex chains of if-then-else.
Your first match expression
day match {
1 -> "Monday",
2 -> "Tuesday",
3 -> "Wednesday",
_ -> "Other day",
}
where { day = 2 }
This checks day against each pattern and returns the matching result. The underscore _ is a wildcard that matches anything.
Result: "Tuesday"
The structure
VALUE match {
PATTERN1 -> RESULT1,
PATTERN2 -> RESULT2,
PATTERN3 -> RESULT3,
}
Melbi tries each pattern from top to bottom and uses the first match.
Literal patterns
Match exact values:
Numbers
score match {
100 -> "Perfect!",
90 -> "Excellent",
80 -> "Good",
_ -> "Keep trying",
}
where { score = 90 }
Strings
command match {
"start" -> "Starting...",
"stop" -> "Stopping...",
"restart" -> "Restarting...",
_ -> "Unknown command",
}
where { command = "start" }
Booleans
flag match {
true -> "Enabled",
false -> "Disabled",
}
where { flag = true }
Wildcard pattern: _
The underscore matches anything and is typically used as the last case:
status_code match {
200 -> "Success",
404 -> "Not Found",
500 -> "Server Error",
_ -> "Other status",
}
where { status_code = 403 }
Result: "Other status"
Think of _ as “anything else” or “default case”.
Variable patterns
Bind the value to a name:
result match {
value -> value * 2,
}
where { result = 21 }
This binds result to the name value, then doubles it. Result: 42.
This is most useful when you want to transform the value:
input match {
x -> f"You entered: { x }",
}
where { input = "hello" }
Option patterns
We’ve seen these before:
maybe_user match {
some name -> f"Hello, { name }!",
none -> "Hello, guest!",
}
where { maybe_user = some "Alice" }
You can also nest them:
outer match {
some (some value) -> f"Double some: { value }",
some none -> "Some none",
none -> "Just none",
}
where { outer = some (some 42) }
Exhaustiveness checking
Melbi is smart about patterns. For certain types, it ensures you handle all cases:
Booleans
flag match {
true -> "yes",
false -> "no",
}
Both cases covered! ✓
If you forget one:
flag match {
true -> "yes",
}
Melbi will complain - you must handle false (or use _ wildcard).
Options
opt match {
some x -> x,
none -> 0,
}
Both cases covered! ✓
Other types need a wildcard
For numbers, strings, etc., you need a catch-all:
n match {
1 -> "one",
2 -> "two",
_ -> "many",
}
The _ makes it exhaustive.
Practical examples
HTTP status handling
response match {
200 -> { success = true, message = "OK" },
201 -> { success = true, message = "Created" },
404 -> { success = false, message = "Not Found" },
500 -> { success = false, message = "Server Error" },
_ -> { success = false, message = "Unknown Error" },
}
where { response = 404 }
Grade mapping
letter match {
"A" -> 4.0,
"B" -> 3.0,
"C" -> 2.0,
"D" -> 1.0,
"F" -> 0.0,
_ -> 0.0,
}
where { letter = "B" }
State machine
state match {
"idle" -> "Ready",
"loading" -> "Please wait...",
"success" -> "Complete!",
"error" -> "Something went wrong",
_ -> "Unknown state",
}
where { state = "loading" }
Permission levels
role match {
"admin" -> 100,
"moderator" -> 50,
"user" -> 10,
"guest" -> 1,
_ -> 0,
}
where { role = "moderator" }
Matching with complex expressions
You can match on the result of calculations:
(age / 10) match {
0 -> "Child",
1 -> "Teenager",
2 -> "Young Adult",
_ -> "Adult",
}
where { age = 25 }
(score >= 90) match {
true -> "A",
false -> "Not an A",
}
where { score = 95 }
Combining with where bindings
message where {
user_type = "premium",
discount = user_type match {
"premium" -> 0.20,
"standard" -> 0.10,
"trial" -> 0.05,
_ -> 0.0,
},
message = f"Your discount: { discount * 100 }%",
}
Real-world examples
Payment processing
payment_status match {
"completed" -> f"Payment of ${ amount } successful",
"pending" -> "Payment is being processed",
"failed" -> "Payment failed, please try again",
"refunded" -> f"Refund of ${ amount } issued",
_ -> "Unknown payment status",
}
where {
payment_status = "completed",
amount = 99.99,
}
Result: "Payment of $99.99 successful"
File type detection
extension match {
".jpg" -> "JPEG Image",
".png" -> "PNG Image",
".gif" -> "GIF Image",
".pdf" -> "PDF Document",
".txt" -> "Text File",
_ -> "Unknown File Type",
}
where { extension = ".jpg" }
Result: "JPEG Image"
Game action handling
action match {
"attack" -> f"Dealt { damage } damage",
"defend" -> "Raised shield",
"heal" -> "Restored health",
"flee" -> "Ran away",
_ -> "Invalid action",
}
where {
action = "attack",
damage = 50,
}
Result: "Dealt 50 damage"
API error codes
error_code match {
"AUTH_FAILED" -> "Invalid username or password",
"RATE_LIMITED" -> "Too many requests, please wait",
"NOT_FOUND" -> "Resource not found",
"NETWORK_ERROR" -> "Connection failed",
_ -> "An error occurred",
}
where { error_code = "AUTH_FAILED" }
Result: "Invalid username or password"
Pattern matching vs if-then-else
Compare these two approaches:
With if-then-else:
if status == 200 then "OK"
else if status == 404 then "Not Found"
else if status == 500 then "Error"
else "Unknown"
where { status = 404 }
With pattern matching:
status match {
200 -> "OK",
404 -> "Not Found",
500 -> "Error",
_ -> "Unknown",
}
where { status = 404 }
Pattern matching is:
- More concise
- Easier to read
- Safer (exhaustiveness checking)
- More maintainable
When to use pattern matching
Use match when:
- You’re handling multiple specific cases
- Working with Options or other variant types
- Want exhaustiveness checking
- Need cleaner, more declarative code
Use if-then-else when:
- You have just one or two conditions
- Conditions are complex comparisons (like
x > 10 and y < 20) - You’re combining multiple boolean checks
Often you’ll use both in the same expression!
result match {
some value -> if value > 100 then "High" else "Normal",
none -> "Unknown",
}
where { result = some 150 }
Try it yourself!
Create expressions that:
- Match day numbers (1-7) to day names
- Convert HTTP methods (“GET”, “POST”, etc.) to descriptions
- Handle game results (“win”, “lose”, “draw”) with scores
- Map error codes to user-friendly messages
- Classify ages into categories with pattern matching
You’ve mastered pattern matching! You can now handle complex branching logic elegantly and safely.
Next, you’ll learn about Type Casting - converting between different types explicitly!