jonnifyfsharp

Module C3.03

Pattern matching, sharpened

Matching is the habit that crosses unchanged — then the compiler starts checking it.

Pattern matching is the most familiar bridge from Elixir. You already match {:ok, v} against {:error, _}, destructure tuples and maps, and guard with when. F# does all of it the same way — case … do … end becomes match … with, the ;-clauses become |-arms, and the shapes are identical. What F# adds is two powers Elixir does not have at compile time. The compiler checks that a match covers every case and reports the missing one before the program runs (the FS0025 warning); and active patterns let you name a match case with your own logic and use it like a built-in. This module takes the three in turn.

The same construct, then two new powers

An Elixir developer reads a match arm on sight: it is the case they write every day. So the first dive is a tour of familiar ground — the tagged tuple, the list head/tail, the record, the when guard, all of it — in F# syntax. Nothing to unlearn.

The next two dives are where F# does more. Exhaustiveness: in Elixir a case that omits a clause compiles and raises CaseClauseError only when an unhandled value arrives at run time; in F# the compiler reads the union and reports the missing case at compile time. Active patterns: a small function in banana brackets (| … |) that the matcher treats as a pattern — the F# answer to a chain of Elixir cond clauses or inline guards, named once and matched like a built-in.

The three dives · select one

C3.03.1 familiar ground C3.03.2 exhaustiveness C3.03.3 active patterns the same case compile-time check your own case
C3.03.1 · Familiar ground

The tagged tuple, the list head/tail, the record, the when guard — everything you match on in Elixir, in F# syntax. case do … end becomes match … with.

You learn the Elixir match habits, in F# form

Open this dive · or pick another above

The match arm is the one piece of F# an Elixir developer reads with no translation. The compiler's checking of it is the part that is new.

The same match, in one block each

The bridge is concrete. Where Elixir writes a case over a tagged tuple, F# writes a match over a Result. The clause order, the wildcard _, the bind on the matched value — all the same. The F# echo below was produced by dotnet fsi.

Elixir — case over a tagged tuple

# Elixir
case result do
  {:ok, v} -> v
  {:error, _} -> 0
end

F# — match over a Result

let describe result =
    match result with
    | Ok v -> v
    | Error _ -> 0
// describe (Ok 42)        -> 42
// describe (Error "boom") -> 0

Elixir habit

case … do … end with {:ok, v} / {:error, _} clauses — checked when a value arrives at run time.

F# form

match … with over Ok v / Error _ — the same shape, checked for completeness at compile time, extensible with active patterns.

The three dives

Read them in order. Each stands on valid F# beside the real Elixir it parallels: every // echo below was produced by dotnet fsi, and the FS0025 warning text in the second dive is the line the compiler actually emits. Together they take an Elixir developer from the case they know to the two things F# adds on top of it.

live module hub built dive ready soon specified

References

Sources

Related in this course