Reading — step 1 of 7
Learn
Algebraic data types are how Haskell models domain data. The data keyword creates new types with sum (alternatives) and product (combinations) shapes. LYAH treats this as foundational; Real World Haskell uses it from chapter 3 onward.
Sum types — alternatives
data Color = Red | Green | Blue
Color is a new type with three constructors — Red, Green, Blue. They take no arguments — just enum-like tags.
favorite :: Color
favorite = Blue
describe :: Color -> String
describe Red = "warm"
describe Green = "natural"
describe Blue = "cool"
Pattern matching is exhaustive — the compiler warns if a case is missing.
Constructors with data
data Shape = Circle Double | Square Double | Rectangle Double Double
area :: Shape -> Double
area (Circle r) = pi * r * r
area (Square s) = s * s
area (Rectangle w h) = w * h
Each constructor takes its own arguments. Circle 5 is a Shape; so is Rectangle 3 4. Pattern matching destructures them.
This is a sum of products — Shape is one of three (sum), each with its own fields (product).
Records
When a constructor has many fields, named accessors help:
data Person = Person { name :: String, age :: Int, email :: String }
ada :: Person
ada = Person { name = "Ada", age = 36, email = "[email protected]" }
-- Accessors are auto-generated:
name ada -- "Ada"
age ada -- 36
-- Update syntax — produces a new Person:
older = ada { age = 37 }
Record syntax generates name :: Person -> String etc. for free.
Type parameters (generics)
data Maybe a = Nothing | Just a
The a is a TYPE PARAMETER. Maybe Int is one type, Maybe String is another. Pattern match:
safeDivide :: Int -> Int -> Maybe Int
safeDivide _ 0 = Nothing
safeDivide a b = Just (a `div` b)
case safeDivide 10 0 of
Just n -> putStrLn ("got " ++ show n)
Nothing -> putStrLn "divide by zero"
Maybe is built-in; this is just showing how it would be defined.
Recursive types
data Tree a = Leaf | Node a (Tree a) (Tree a)
depth :: Tree a -> Int
depth Leaf = 0
depth (Node _ l r) = 1 + max (depth l) (depth r)
A Tree of a is either a Leaf or a Node with a value and two subtrees. Recursive — the (Tree a) references itself.
Deriving — automatic instances
data Point = Point Int Int deriving (Show, Eq, Ord)
deriving auto-generates instances for common type classes:
Show— printable (show p)Eq— equality (==,/=)Ord— ordering (<,>)Read— parseable from StringEnum— successor/predecessorBounded— minBound/maxBound
Without deriving Show, print p won't compile. Almost always include Show, Eq at minimum.
When to use ADTs
- Domain modeling:
data Status = Active | Pending | Archived - Result types:
data Result a = Ok a | Err String - AST nodes:
data Expr = Lit Int | Add Expr Expr | Mul Expr Expr - State machines:
data Order = Cart | Confirmed | Shipped | Delivered
ADTs + pattern matching = type-safe state machines. Add a new variant; the compiler tells you every match site that needs updating.
Common mistakes
- Forgetting
deriving Show—printand many other things won't compile. - Constructor name shadows type name —
data Foo = Foo Intis fine but confusing initially. Constructor and type can share names. - Capitalization — type names AND constructor names are uppercase. Type variables (the
ainMaybe a) are lowercase. - Mixing Maybe with
Nothing—Nothingis the constructor;Maybeis the type. UseMaybe Int -> Maybe Intin signatures. - Plain enums when records would clarify — for things with multiple fields, records are more maintainable than positional constructors.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…