Haskell's type system lets you define rich domain models and attach behaviors via type classes. Today you create custom data types, make them instances of standard type classes, and see how the type system enforces program correctness.
ADTs model domain concepts precisely. 'data Shape = Circle Double | Rectangle Double Double | Triangle Double Double Double' is a sum type — a Shape is one of three variants. 'data Person = Person { name :: String, age :: Int }' is a product type with named fields (record syntax). Pattern match on constructors to handle each case. The compiler warns if you miss a case.
Add 'deriving (Eq, Ord, Show, Read, Enum, Bounded)' to automatically generate standard instances. Eq: (==) and (/=). Ord: (<), compare. Show: show turns a value to a String. Read: read parses a String. Deriving handles nested types automatically. For custom behavior, write a manual instance: 'instance Show Shape where show (Circle r) = ...'
A type class defines an interface. 'class Container f where empty :: f a; insert :: a -> f a -> f a; toList :: f a -> [a]'. Types implement it: 'instance Container [] where ...'. Functor, Foldable, and Traversable are the standard type classes for container types. A type that is a Functor can use fmap; a Foldable can use foldr/sum/length; a Traversable can sequence effects through a structure.
data Shape
= Circle Double
| Rectangle Double Double
| Triangle Double Double Double
deriving (Show, Eq)
area :: Shape -> Double
area (Circle r) = pi * r * r
area (Rectangle w h) = w * h
area (Triangle a b c) =
let s = (a + b + c) / 2
in sqrt (s * (s-a) * (s-b) * (s-c)) -- Heron's formula
perimeter :: Shape -> Double
perimeter (Circle r) = 2 * pi * r
perimeter (Rectangle w h) = 2 * (w + h)
perimeter (Triangle a b c) = a + b + c
-- Type class instance for ordering by area
instance Ord Shape where
compare s1 s2 = compare (area s1) (area s2)
-- Usage
shapes :: [Shape]
shapes = [Circle 5, Rectangle 3 4, Triangle 3 4 5]
largest :: Shape
largest = maximum shapes -- uses Ord instance
Design an ADT for a simple expression language: numbers, variables, addition, multiplication, and let-bindings. Write an eval :: Expr -> Map String Int -> Int evaluator and a pretty :: Expr -> String pretty-printer.