Implementing Instances
Instances connect types to type classes. Learn to write them for your own types and understand how Lean resolves them.
Basic Instance Syntax
Declare an instance with instance:
1structure Point where2 x : Float3 y : Float45-- Anonymous instance (most common)6instance : ToString Point where7 toString p := s!"({p.x}, {p.y})"89-- Named instance (useful for debugging)10instance instBEqPoint : BEq Point where11 beq p1 p2 := p1.x == p2.x && p1.y == p2.y1213#eval toString (Point.mk 3.0 4.0) -- "(3.0, 4.0)"14#eval Point.mk 1.0 2.0 == Point.mk 1.0 2.0 -- trueImplementing Common Type Classes
BEq: Equality
The BEq type class provides the == operator. You implement the beq method which takes two values and returnstrue if they should be considered equal.
1inductive Color where2 | red | green | blue34instance : BEq Color where5 beq c1 c2 := match c1, c2 with6 | .red, .red => true7 | .green, .green => true8 | .blue, .blue => true9 | _, _ => false1011#eval Color.red == Color.red -- true12#eval Color.red == Color.blue -- falseOrd: Ordering
The Ord type class enables the comparison operators<, >, <=, and >=. You implement compare which returns .lt, .eq, or .gt.
1instance : Ord Color where2 compare c1 c2 := match c1, c2 with3 | .red, .red => .eq4 | .red, _ => .lt5 | .green, .red => .gt6 | .green, .green => .eq7 | .green, .blue => .lt8 | .blue, .blue => .eq9 | .blue, _ => .gt1011#eval compare Color.red Color.blue -- .lt12#eval Color.green < Color.blue -- trueHashable
The Hashable type class enables using your type as keys inHashMap and elements in HashSet. You must ensure that equal values produce equal hashes.
1instance : Hashable Color where2 hash c := match c with3 | .red => 04 | .green => 15 | .blue => 2Repr: Debug Representation
The Repr type class controls how values appear in #evaloutput and debugging. Unlike ToString, Repr typically shows the constructor form so you can copy-paste the output back as valid code.
1instance : Repr Point where2 reprPrec p _ := s!"Point.mk {p.x} {p.y}"34#eval (Point.mk 1.0 2.0 : Point) -- Point.mk 1.0 2.0Instances for Parameterized Types
When your type has parameters, instances can require constraints on those parameters:
1structure Pair (α β : Type) where2 fst : α3 snd : β45-- Require both types to have BEq6instance [BEq α] [BEq β] : BEq (Pair α β) where7 beq p1 p2 := p1.fst == p2.fst && p1.snd == p2.snd89-- Now this works for any Pair of equatable types10#eval Pair.mk 1 "a" == Pair.mk 1 "a" -- true11#eval Pair.mk 1 "a" == Pair.mk 2 "a" -- falsePair needs BEqfor its elements, then Pair Nat String works becauseNat and String have BEq instances.Recursive Instances
1-- Instance for List requires instance for elements2instance [BEq α] : BEq (List α) where3 beq xs ys := match xs, ys with4 | [], [] => true5 | x :: xs, y :: ys => x == y && beq xs ys6 | _, _ => false78#eval [1, 2, 3] == [1, 2, 3] -- true9#eval [1, 2] == [1, 2, 3] -- falseUsing deriving
For simple types, deriving generates instances automatically:
1-- Derive multiple instances at once2structure Person where3 name : String4 age : Nat5 deriving Repr, BEq, Hashable67-- Works immediately8#eval Person.mk "Alice" 30 == Person.mk "Alice" 30 -- true9#eval repr (Person.mk "Bob" 25) -- Person.mk "Bob" 251011-- For inductive types too12inductive Status where13 | pending14 | active15 | completed16 deriving Repr, BEq, Hashable, Ordderiving when possible—it's less error-prone than manual instances. Write manual instances for custom behavior.Default Method Implementations
Type classes can provide default implementations:
1class Printable (α : Type) where2 format : α → String3 -- Default implementation using format4 print : α → IO Unit := fun x => IO.println (format x)56-- Only need to implement format7instance : Printable Nat where8 format n := s!"Number: {n}"910-- print comes for free11#eval (Printable.print 42 : IO Unit) -- prints "Number: 42"Instance Priority
When multiple instances could apply, priority determines which wins:
1-- Lower number = higher priority (tried first)2instance (priority := high) : ToString Bool where3 toString b := if b then "yes" else "no"45instance (priority := low) : ToString Bool where6 toString b := if b then "1" else "0"78-- The high priority instance wins9#eval toString true -- "yes"Debugging Instance Search
If Lean can't find an instance, the error message points to the missing class. Check whether you forgot to import a module or define an instance.
Deep Dive: Instance Search
When Lean sees x == y, it:
- Determines the type of
x - Searches for a
BEqinstance for that type - If the type has parameters, recursively finds instances for them
- Uses the found instance to call
BEq.beq
This happens at compile time, so there's no runtime overhead.
Orphan Instances
An "orphan instance" is an instance for a type you don't own, in a module that doesn't define the type or the class. Use with caution:
1-- In your module: instance for external type2-- This is an "orphan" - can cause conflicts34instance : ToString (List Nat) where5 toString xs := s!"[{String.intercalate ", " (xs.map toString)}]"67-- Better: wrap in your own type8structure MyList (α : Type) where9 items : List α1011instance [ToString α] : ToString (MyList α) where12 toString xs := s!"MyList: {xs.items}"Define a BEq instance for a simple structure.
1structure User where2 id : Nat3 name : String45instance : BEq User where6 beq a b := a.id == b.id && a.name == b.name78#eval ({ id := 1, name := "A" } : User) == { id := 1, name := "A" }