Gleam Guards

Guards add extra conditions to pattern match branches. A pattern describes the shape of a value; a guard further tests whether the pattern's bound variables satisfy a boolean condition. Together, they make case expressions precise and expressive.

What Is a Guard?

A guard is the if clause that appears after a pattern in a case branch:

case value {
  pattern if <boolean condition> -> result
  _                              -> fallback
}

A branch with a guard matches only when both the pattern and the guard condition are true. If the pattern matches but the guard fails, Gleam moves to the next branch.


Guard Evaluation Flow
──────────────────────────────────────────────────
case score {
  n if n >= 90 -> "A"
  n if n >= 80 -> "B"
  _            -> "Other"
}

score = 85

Branch 1: n = 85, 85 >= 90? No → skip
Branch 2: n = 85, 85 >= 80? Yes → match! → "B"

Guards with Numeric Ranges

pub fn classify_bmi(bmi: Float) -> String {
  case bmi {
    b if b <. 18.5              -> "Underweight"
    b if b >=. 18.5 && b <. 25.0 -> "Normal"
    b if b >=. 25.0 && b <. 30.0 -> "Overweight"
    _                            -> "Obese"
  }
}

BMI Classification Chart
──────────────────────────────────────────────────
BMI Range     │ Category
──────────────┼──────────────────
Below 18.5    │ Underweight
18.5 – 24.9   │ Normal
25.0 – 29.9   │ Overweight
30.0 and above│ Obese

Guards with String Conditions

import gleam/string

pub fn validate_username(name: String) -> String {
  case name {
    n if string.length(n) == 0       -> "Username cannot be empty"
    n if string.length(n) < 3        -> "Too short — minimum 3 characters"
    n if string.length(n) > 20       -> "Too long — maximum 20 characters"
    _                                 -> "Username is valid"
  }
}

Guards with Multiple Conditions

Combine conditions in a guard using && and ||:

pub fn ticket_price(age: Int, is_student: Bool) -> Float {
  case age {
    a if a < 5                           -> 0.0
    a if a >= 5 && a <= 12               -> 50.0
    a if a >= 13 && a <= 17              -> 100.0
    _ if is_student                      -> 150.0
    a if a >= 60                         -> 150.0
    _                                    -> 250.0
  }
}

Ticket Pricing Matrix
──────────────────────────────────────────────────
Age Range    │ Student? │ Price
─────────────┼──────────┼────────
Under 5      │ Any      │ Free
5 – 12       │ Any      │ ₹50
13 – 17      │ Any      │ ₹100
Any          │ Yes      │ ₹150
60+          │ Any      │ ₹150
Default      │ No       │ ₹250

Guards Cannot Produce Side Effects

Guards must be pure boolean expressions. They cannot call functions that print, write files, or perform network requests. Functions used in guards must return Bool and have no side effects.


Valid Guard Expressions
──────────────────────────────────────────────────
✓ n > 0
✓ n >= 10 && n <= 100
✓ string.length(s) > 3
✓ list.length(items) == 0
✓ is_valid(x)   ← only if is_valid returns Bool

Invalid Guard Expressions
──────────────────────────────────────────────────
✗ io.println("checking")  ← side effect
✗ let x = 5               ← binding not allowed

Guards with Captured Variables

When a pattern binds a variable, that variable is available in the guard:

let items = ["apple", "banana", "cherry"]

case items {
  [first, ..rest] if string.starts_with(first, "a") ->
    "Starts with A: " <> first
  [first, ..rest] ->
    "First item: " <> first
  [] ->
    "Empty list"
}
// "Starts with A: apple"

Practical Example — Loan Eligibility

import gleam/io

pub fn loan_decision(age: Int, income: Float, credit_score: Int) -> String {
  case #(age, credit_score) {
    #(a, _) if a < 21          -> "Too young for a loan"
    #(_, cs) if cs < 600       -> "Credit score too low"
    #(a, cs) if a >= 21 && cs >= 750 && income >=. 50000.0 ->
      "Approved — Premium rate"
    #(a, cs) if a >= 21 && cs >= 650 && income >=. 30000.0 ->
      "Approved — Standard rate"
    _                          -> "Not eligible"
  }
}

pub fn main() {
  io.println(loan_decision(25, 60000.0, 780))  // Approved - Premium rate
  io.println(loan_decision(19, 45000.0, 700))  // Too young
  io.println(loan_decision(30, 25000.0, 620))  // Not eligible
}

Summary


Guards Quick Reference
──────────────────────────────────────────────────
Syntax:   pattern if condition -> result

Rules:
  • Condition must be a Bool expression
  • Bound pattern variables are available in guard
  • Multiple conditions: use && and ||
  • No side effects in guards
  • If guard fails, Gleam tries the next branch

Guards turn simple pattern matching into a full decision engine. Combined with expressive patterns, they let you encode business rules directly in the structure of your code — where the compiler keeps them honest.

Leave a Comment

Your email address will not be published. Required fields are marked *