Skip to content

Pattern Matching

Pattern matching is a powerful control flow construct in Harneet that allows you to match values against patterns and execute code based on the match. It provides a more expressive and safer alternative to traditional switch statements.

Basic Syntax

The basic syntax for pattern matching uses the match keyword:

Basic Syntax
1
2
3
4
5
6
package main
match value {
    pattern1 => expression1,
    pattern2 => expression2,
    _ => default_expression
}

Pattern Types

Literal Patterns

Match against specific literal values:

Literal Patterns
1
2
3
4
5
6
7
package main
var status = match code {
    200 => "OK",
    404 => "Not Found", 
    500 => "Server Error",
    _ => "Unknown"
}

Wildcard Pattern

The underscore _ matches any value and acts as a catch-all:

Wildcard Pattern
1
2
3
4
5
6
package main
var result = match value {
    1 => "one",
    2 => "two",
    _ => "other"  // Matches anything else
}

Variable Binding

Bind the matched value to a variable:

Variable Binding
1
2
3
4
5
6
package main
var message = match number {
    0 => "zero",
    1 => "one",
    n => "some number"  // 'n' contains the matched value
}

Array Destructuring

Match and destructure arrays:

Array Patterns
1
2
3
4
5
6
7
8
package main
var description = match coordinates {
    [] => "empty",
    [x] => "single point",
    [x, y] => "2D point", 
    [x, y, z] => "3D point",
    _ => "higher dimension"
}

You can also use specific values in array patterns:

Array Value Matching
1
2
3
4
5
6
7
package main
var result = match point {
    [0, 0] => "origin",
    [x, 0] => "on x-axis",
    [0, y] => "on y-axis", 
    [x, y] => "general point"
}

Blank Identifiers in Patterns

Use _ to ignore specific values in destructuring:

Ignore Values
1
2
3
4
5
6
package main
var info = match data {
    [_, _, important] => "third element is important",
    [first, _] => "only care about first element",
    _ => "other"
}

Guard Clauses

Add conditional logic to patterns using if guards:

Guards
1
2
3
4
5
6
7
8
package main
var category = match age {
    a if a < 13 => "child",
    a if a >= 13 and a < 20 => "teenager",
    a if a >= 20 and a < 65 => "adult",
    a if a >= 65 => "senior",
    _ => "unknown"
}

Guards can use any boolean expression:

Complex Guards
1
2
3
4
5
6
7
8
package main
var grade = match score {
    s if s >= 90 => "A",
    s if s >= 80 => "B",
    s if s >= 70 => "C", 
    s if s >= 60 => "D",
    s => "F"
}

Array Pattern Examples

Basic Array Matching

Basic Array Matching
package main
import fmt
var arrays = [[], [1], [1, 2], [1, 2, 3]]

for arr in arrays {
    var description = match arr {
        [] => "empty array",
        [_] => "single element",
        [_, _] => "pair",
        [1, 2, 3] => "specific triple",
        _ => "other array"
    }
    fmt.Printf("Array %s => %s\n", arr, description)
}

Array Destructuring with Guards

Array Guards
package main
import fmt
var points = [[0, 0], [5, 0], [0, 3], [4, 3]]

for point in points {
    var quadrant = match point {
        [0, 0] => "origin",
        [x, 0] if x > 0 => "positive x-axis",
        [x, 0] if x < 0 => "negative x-axis",
        [0, y] if y > 0 => "positive y-axis", 
        [0, y] if y < 0 => "negative y-axis",
        [x, y] if x > 0 and y > 0 => "first quadrant",
        [x, y] if x < 0 and y > 0 => "second quadrant",
        [x, y] if x < 0 and y < 0 => "third quadrant",
        [x, y] if x > 0 and y < 0 => "fourth quadrant",
        [x, y] => "some point"
    }
    fmt.Printf("Point %s => %s\n", point, quadrant)
}

Real-World Examples

HTTP Status Code Processing

HTTP Status Codes
package main
import fmt
var statusCodes = [200, 404, 500, 301, 401]

for code in statusCodes {
    var status = match code {
        200 => "OK",
        201 => "Created",
        301 => "Moved Permanently", 
        400 => "Bad Request",
        401 => "Unauthorized",
        404 => "Not Found",
        500 => "Internal Server Error",
        c if c >= 200 and c < 300 => "Success",
        c if c >= 300 and c < 400 => "Redirection",
        c if c >= 400 and c < 500 => "Client Error", 
        c if c >= 500 => "Server Error",
        _ => "Unknown Status"
    }
    fmt.Printf("HTTP %d => %s\n", code, status)
}

Role-Based Access Control

Role-Based Access
package main
import fmt
var roles = ["admin", "user", "guest", "moderator"]

for role in roles {
    var access = match role {
        "admin" => "full access",
        "moderator" => "limited admin access",
        "user" => "standard access",
        "guest" => "read-only access",
        _ => "no access"
    }
    fmt.Printf("Role '%s' => %s\n", role, access)
}

Type Safety and Exhaustiveness

Pattern matching in Harneet includes compile-time type checking:

  • Pattern Compatibility: Patterns must be compatible with the matched value type
  • Variable Binding Types: Variables bound in patterns get proper types
  • Guard Validation: Guard clauses must be boolean expressions
  • Return Type Consistency: All match arms must return compatible types
  • Exhaustiveness Warnings: The compiler warns about potentially non-exhaustive matches

Best Practices

1. Always Include a Wildcard

Include Wildcard
// Good - handles all cases
package main
import fmt
var result = match value {
    "expected" => "handled",
    _ => "fallback"
}

// Risky - may not handle all cases
var result = match value {
    "expected" => "handled"
    // Missing wildcard
}

2. Use Guards for Complex Conditions

Use Guards
// Good - clear and expressive
package main
var category = match age {
    a if a < 18 => "minor",
    a if a >= 65 => "senior", 
    a => "adult"
}

// Less clear with nested conditions
if age < 18 {
    category = "minor"
} else if age >= 65 {
    category = "senior"
} else {
    category = "adult"
}

3. Use Destructuring for Data Extraction

Use Destructuring
// Good - extract values directly
package main
var info = match coordinates {
    [x, y] => fmt.Sprintf("Point at (%d, %d)", x, y),
    _ => "Invalid coordinates"
}

// More verbose alternative
if len(coordinates) == 2 {
    var x = coordinates[0]
    var y = coordinates[1]
    info = fmt.Sprintf("Point at (%d, %d)", x, y)
} else {
    info = "Invalid coordinates"
}

4. Leverage Type Safety

Pattern matching provides compile-time guarantees about type safety and helps catch errors early in development.

Comparison with Switch Statements

Pattern matching is more powerful than traditional switch statements:

Feature Switch Pattern Matching
Value matching
Variable binding
Destructuring
Guard clauses
Type safety Limited
Exhaustiveness checking

Pattern matching provides a more expressive, safer, and more powerful way to handle conditional logic in Harneet programs.