Matching on Constructors and Literals
Pattern matching lets you define a function as a series of equations, each matching a different shape of input, rather than writing nested if statements — describe 0 = "zero" and describe n = "nonzero: " ++ show n dispatch on whether the argument literally equals 0 or falls through to the catch-all variable pattern n. The same mechanism destructures algebraic data types: given data Shape = Circle Double | Rectangle Double Double, a function area (Circle r) = pi * r * r and area (Rectangle w h) = w * h pulls the radius or width/height straight out of the constructor without calling a separate accessor function. GHC checks pattern matches for exhaustiveness and will warn (or error, with -Wall -Werror) if you forget a constructor, catching a class of bugs that's easy to miss with manual if/else chains.
Cricket analogy: Pattern matching a dismissal type is like an umpire's decision tree: 'bowled' matches one pattern, 'caught behind' matches another, and 'lbw' a third — each shape of dismissal is handled by its own named rule rather than one giant nested if.
Deconstructing Lists and Tuples
Lists have their own natural patterns built from [] and :, so sumList [] = 0 and sumList (x:xs) = x + sumList xs matches the empty list as the base case and splits any non-empty list into its head x and tail xs for the recursive case. Tuples destructure positionally, so midpoint (x1, y1) (x2, y2) = ((x1+x2)/2, (y1+y2)/2) pulls out coordinates without any explicit fst/snd calls, and nested patterns like (x:_:rest) let you grab the first element, skip the second with the wildcard _, and bind the remainder in a single line. Guards can be combined with patterns for extra precision, as in classify (x:xs) | x > 0 = "starts positive", letting you match structurally and then further refine with a boolean condition.
Cricket analogy: Matching (x:xs) on a list of scores is like an analyst pulling the first ball of an over separately from the remaining five balls, to comment on the opening delivery before summarizing the rest.
data Shape = Circle Double | Rectangle Double Double
area :: Shape -> Double
area (Circle r) = pi * r * r
area (Rectangle w h) = w * h
sumList :: [Int] -> Int
sumList [] = 0
sumList (x:xs) = x + sumList xs
describeFirst :: [Int] -> String
describeFirst (x:_) | x > 0 = "starts positive"
| x < 0 = "starts negative"
describeFirst (0:_) = "starts with zero"
describeFirst [] = "empty list"Case Expressions and Exhaustiveness
The case expression brings the same pattern-matching power into any expression position, not just top-level function equations: case shape of Circle r -> ...; Rectangle w h -> ... lets you match locally, inside a let or where, or as an argument to another function. Because Haskell's type system knows every constructor of a data type, GHC's exhaustiveness checker can prove at compile time whether a case covers every possibility — enabling -Wincomplete-patterns to flag a forgotten branch before the program ever runs, unlike dynamically typed languages where a missing else branch might only surface as a runtime crash. This compile-time safety net is one of the biggest practical reasons Haskell developers lean on algebraic data types and pattern matching instead of manual type tags and casts.
Cricket analogy: A case expression is like a match referee's decision matrix used anywhere in the report, not just the final scorecard — 'six', 'four', 'dot ball', or 'wicket' can each be matched and handled inline wherever the commentary needs it.
The wildcard pattern _ matches anything without binding a name, and is commonly used for the catch-all final equation, e.g. dayType _ = "weekday" after specific matches for "Saturday" and "Sunday" — this keeps the compiler's exhaustiveness checker satisfied while ignoring values you don't need.
Never rely on pattern-match order alone to feel exhaustive without checking — a partial function like firstElement (x:_) = x compiles fine but will crash with a runtime pattern-match failure on an empty list; always enable -Wincomplete-patterns (or -Wall) so GHC warns you about missing cases at compile time instead of finding out in production.
- Pattern matching dispatches on the shape (constructor, literal, or structure) of a value, replacing nested if/else chains.
- Function equations can match multiple patterns, including literals, constructors, and the wildcard
_. - Lists pattern-match naturally via
[]and(x:xs), splitting head and tail in one step. - Tuples destructure positionally, e.g.
(x, y), without needingfst/sndaccessor calls. - Guards (
| condition) can be combined with patterns for finer-grained branching. caseexpressions bring pattern matching into any expression position, not just top-level equations.- GHC's exhaustiveness checker (with
-Wincomplete-patterns) flags missing cases at compile time, preventing runtime crashes.
Practice what you learned
1. What does the pattern `(x:xs)` do when matched against a non-empty list?
2. What is the purpose of the wildcard pattern `_`?
3. What happens if you call a function like `firstElement (x:_) = x` with an empty list?
4. How does GHC's exhaustiveness checking help developers?
5. Which of these is an example of tuple pattern matching?
Was this page helpful?
You May Also Like
Guards and Case Expressions
Learn how boolean guards and case expressions give Haskell readable, structured alternatives to nested if-then-else chains for multi-way branching.
Recursion in Haskell
Understand why recursion is the primary looping mechanism in Haskell, how base cases and recursive cases work, and how tail recursion and laziness affect performance.
Lists and List Comprehensions
Learn how Haskell's linked-list data type works and how list comprehensions let you build, filter, and transform lists with concise, declarative syntax.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics