Sums (Inductive Types)
Inductive types let you define data that can be one of several variants—like enums with data. This is the foundation of functional domain modeling.
Basic Inductive Types
An inductive type lists all possible forms a value can take:
1inductive Weekday where2 | sunday3 | monday4 | tuesday5 | wednesday6 | thursday7 | friday8 | saturday9 deriving Repr1011def today : Weekday := Weekday.wednesday1213#eval today -- Weekday.wednesdayEach variant (called a "constructor") is a possible value of the type. This is similar to enums in other languages.
Inductive Types with Data
Unlike simple enums, constructors can carry data:
1inductive Shape where2 | circle (radius : Float)3 | rectangle (width : Float) (height : Float)4 | triangle (base : Float) (height : Float)5 deriving Repr67def myCircle : Shape := Shape.circle 5.08def myRect : Shape := Shape.rectangle 3.0 4.0910#eval myCircle -- Shape.circle 5.0A Shape can be a circle with a radius, a rectangle with dimensions, or a triangle. This is called a "sum type" because a value is one of the options.
Pattern Matching on Inductives
To use inductive types, you pattern match on their constructors:
1inductive Shape where2 | circle (radius : Float)3 | rectangle (width : Float) (height : Float)4 | triangle (base : Float) (height : Float)56def area : Shape → Float7 | Shape.circle r => 3.14159 * r * r8 | Shape.rectangle w h => w * h9 | Shape.triangle b h => 0.5 * b * h1011#eval area (Shape.circle 5.0) -- 78.5397512#eval area (Shape.rectangle 3.0 4.0) -- 12.013#eval area (Shape.triangle 6.0 4.0) -- 12.0The Option Type
Option is a built-in inductive type for values that might not exist:
1-- Option is defined roughly as:2-- inductive Option (α : Type) where3-- | none : Option α4-- | some : α → Option α56def safeDivide (x y : Nat) : Option Nat :=7 if y == 0 then none else some (x / y)89#eval safeDivide 10 2 -- some 510#eval safeDivide 10 0 -- none1112-- Pattern match to extract the value13def divideOrZero (x y : Nat) : Nat :=14 match safeDivide x y with15 | none => 016 | some result => resultModeling Real Domains
Inductive types excel at representing complex domain logic:
1inductive PaymentMethod where2 | cash3 | creditCard (number : String) (expiry : String)4 | bankTransfer (accountId : String)5 deriving Repr67inductive OrderStatus where8 | pending9 | paid (method : PaymentMethod)10 | shipped (trackingId : String)11 | delivered12 | cancelled (reason : String)13 deriving Repr1415-- Create realistic orders16def order1 : OrderStatus := OrderStatus.pending17def order2 : OrderStatus := OrderStatus.paid (PaymentMethod.creditCard "1234..." "12/25")18def order3 : OrderStatus := OrderStatus.cancelled "Customer request"Notice how the type system prevents invalid states: you can't have a tracking ID without the order being shipped, and you can't have a payment method without the order being paid.
Open vs Closed Keyword
Use open to avoid repeating the type name:
1inductive Color where2 | red | green | blue3 deriving Repr45-- Without open6def withoutOpen : Color := Color.red78-- With open 9open Color in10def withOpen : Color := red1112-- Open for a block13open Color14def c1 : Color := red15def c2 : Color := blueDeep Dive: Inductive vs Structure
Structures are for products: combining multiple pieces of data together (AND). A Point has an x AND a y.
Inductive types are for sums: representing alternatives (OR). A Shape is a circle OR a rectangle.
Both can be combined: a constructor in an inductive type can hold multiple fields (product inside sum), and a structure field can be an inductive type (sum inside product).
Generic Inductive Types
Inductive types can be parameterized over other types:
1-- A result that's either a success value or an error message2inductive Result (α : Type) where3 | ok (value : α)4 | error (message : String)5 deriving Repr67def parseInt (s : String) : Result Nat :=8 match s.toNat? with9 | some n => Result.ok n10 | none => Result.error s!"'{s}' is not a valid number"1112#eval parseInt "42" -- Result.ok 4213#eval parseInt "hello" -- Result.error "'hello' is not a valid number"Boolean Logic with Inductives
Even Bool is an inductive type:
1-- Bool is defined as:2-- inductive Bool where3-- | false : Bool4-- | true : Bool56-- Implement our own not7def myNot : Bool → Bool8 | true => false9 | false => true1011-- Implement and12def myAnd : Bool → Bool → Bool13 | true, true => true14 | _, _ => false1516#eval myNot true -- false17#eval myAnd true false -- false