ADTs, Pattern Matching, Tuples, Guards
Class: CSCE-314
Notes:
1. Motivation: Why Data Modeling Matters
Up to now, we’ve been writing functions — recursion, higher-order operations, purity. Functions are powerful, but they don’t exist in isolation. They need data to work on.
In real-world software, you don’t just pass around single integers and strings. You model things: a Student in a university system, a Transaction in banking, a Shape in a drawing tool.
Haskell gives us algebraic data types (ADTs) to create these models in a way that’s expressive, safe, and composable.
By the end of today, you’ll see how ADTs combined with pattern matching let you model concepts directly in code, almost like writing down the definition in plain English.
- ADT + Pattern matching = model concepts in code
2. Tuples: Quick Grouping of Values
A tuple is a quick way to group values without creating a brand-new type. Think of it like throwing two or three things into a basket just to carry them around together.
Example:
pair :: (Int, String)
pair = (42, "Answer")
Here, (42, "Answer") might be familiar as the joke from The Hitchhiker’s Guide to the Galaxy — the number 42 as the answer to life, the universe, and everything. But in Haskell, it’s just an Int and a String paired together.
Limitation: tuples don’t carry meaning (not descriptive). A (Double, Double) could be a coordinate (x, y), or a vector, or a fraction. Without context, you can’t tell.
Pattern matching lets us pull tuples apart easily:
fst (x, _) = x
snd (_, y) = y
Here, (x, _) means 'take the first element and ignore the second.' The underscore _ is a wildcard pattern — a placeholder that says 'I don’t care about this part.'
- Access with pattern matching:
fst,sndfst: returns the first elementsnd: returns the second element
3. Pattern Matching Basics
Pattern matching describes the shape of data directly in the function definition.
Example with tuples:
addPair :: (Int, Int) -> Int
addPair (x, y) = x + y
This function says: to add a pair, break it into x and y, then return their sum.
Lists use pattern matching too:
head' [] = error "empty list"
head' (x:_) = x
The first case [] matches the empty list. The second case (x:_) matches a non-empty list: x is the first element, _ ignores the rest.
- Declarative: avoids getters
The ':' symbol is called the cons operator (short for construct). It builds lists: 1:2:3:[] is [1,2,3].
4. Guards for More Expressive Logic
Sometimes you want to match conditions, not just structure. Guards let you do this.
Example:
sign :: Int -> String
sign n
| n > 0 = "positive"
| n < 0 = "negative"
| otherwise = "zero"
Guards read like English: if n > 0, then 'positive'; if n < 0, then 'negative'; otherwise 'zero'.
They are cleaner than nested if/else chains when you have multiple cases.
- Readable, concise alternative to nested if/else
5. Algebraic Data Types (ADTs): Custom Data Modeling
Tuples are useful, but ADTs let you define your own descriptive types with multiple forms.
Example:
data Shape = Circle Float | Rectangle Float Float | Square Float
This says: a Shape can be a Circle with a radius, a Rectangle with width and height, or a Square with side length.
Pattern matching pairs naturally with ADTs:
area :: Shape -> Float
area (Circle r) = pi * r * r
area (Rectangle w h) = w * h
area (Square s) = s * s
- Clear, structured handling of each case
Each constructor is handled separately, and the code is self-documenting.
6. Recursive ADTs
ADTs can also be recursive, meaning they contain themselves.
Example:
data IntList = Empty | Cons Int IntList
This means an IntList is either Empty, or it’s Cons (an integer plus another IntList).
Cons means construct. For example, [1,2,3] is shorthand for 1 : (2 : (3 : [])), where : is the cons operator.
Functions follow the recursive structure:
sumList Empty = 0
sumList (Cons x xs) = x + sumList xs
Recursive ADTs underpin many structures: trees, graphs, and beyond.
7. Connecting to Learn You a Haskell
Learn You a Haskell for Great Good! is a beginner-friendly resource with humor and cartoons.
Relevant chapters:
- Chapter 3: Types and Typeclasses (intro to defining types)
- Chapter 4: Syntax in Functions (pattern matching, guards, case expressions).
Encourage students to try the examples in ghci as they read. Learning is strongest when they experiment.
- Recommended resource: Learn You a Haskell for Great Good!
- Friendly explanations + cartoons
- Key chapters: 3 (Types and Typeclasses), 4 (Syntax in Functions)
- Encourage experimenting in it
8. Wrap-up and Connection to Next Steps
Today we moved from tuples → pattern matching → guards → ADTs → recursive ADTs.
Students should now be able to:
- Define custom data types
- Write functions using pattern matching and guards
- Understand recursive ADTs as building blocks of lists and trees
This week:
- Wed: live coding ADTs (shapes, lists)
- Fri: group exercise building a geometry ADT with area/perimeter functions
Big takeaway: you’re learning to model problems in code, not just solve them. That’s a key step toward thinking like a software engineer.