Skip to content
Active Patterns
step 1/5

Reading — step 1 of 5

Learn

~1 min readActive Patterns and Computation Expressions

Active patterns extend pattern matching with custom logic. F# unique-among-mainstream feature.

Single-case active pattern (data extraction):

let (|Lower|) (s: string) = s.ToLower()

let greet name =
    match name with
    | Lower "alice" -> "hi alice"     // matches if .ToLower() = "alice"
    | _ -> "hi stranger"

Multi-case (choose one of N variants):

let (|Even|Odd|) n =
    if n % 2 = 0 then Even else Odd

let describe n =
    match n with
    | Even -> sprintf "%d is even" n
    | Odd -> sprintf "%d is odd" n

Partial active patterns — may not match (returns Option):

let (|Int|_|) (s: string) =
    match System.Int32.TryParse s with
    | true, n -> Some n
    | _ -> None

let parse input =
    match input with
    | Int n when n > 0 -> sprintf "positive int: %d" n
    | Int n -> sprintf "non-positive int: %d" n
    | _ -> "not an int"

The |_| says "or doesn't match" — the pattern's success is conditional.

Parameterized — take additional arguments:

let (|DivisibleBy|_|) divisor n =
    if n % divisor = 0 then Some () else None

let fizzBuzz n =
    match n with
    | DivisibleBy 15 -> "FizzBuzz"
    | DivisibleBy 3 -> "Fizz"
    | DivisibleBy 5 -> "Buzz"
    | _ -> string n

Note () means "matched, but no value extracted."

Use cases:

  • Parsing (Int, Email, Date)
  • Domain extraction (UserId, OrderState)
  • Regex pattern matching (libraries provide (|Regex|_|))
  • Test assertions

Active patterns combine the readability of pattern matching with custom predicates — what is X checks would do in less expressive languages.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…