Formal Software Verification

6.5. Type Classes🔗

A type class is a structure of operations parameterised by one or more arguments, usually types. A class declares the operations, an instance supplies them for particular arguments, and instance resolution finds the right instance from those arguments whenever a function requests one with [C α]. Some classes are indexed by more than a type, and Std.Associative op of Lecture 4 is indexed by an operation. This is the mechanism Lecture 2 used to give its meaning through a Membership instance and Lecture 4 used to register add as associative and commutative for ac_rfl.

namespace Func class Size (α : Type) where size : α instance {α : Type} : Size (List α) where size xs := xs.length instance {α : Type} : Size (Option α) where size | none => 0 | some _ => 1 def usize {α : Type} [Size α] (a : α) : := Size.size a end Func

The function usize names no instance. It requests [Size α], and resolution supplies the list instance or the option instance according to the type at the call site.

6.5.1. Examples🔗

The examples below declare instances, watch resolution choose by type, and name the classes the earlier lectures used.

Example 1. The list instance measures a list by its length.

namespace Func 3#eval usize [1, 2, 3] end Func
3

Example 2. The option instance measures presence, 1 or 0.

namespace Func 1#eval usize (some 7) end Func
1

Example 3. An instance may depend on other instances, as a product depends on its factors.

namespace Func instance {α β : Type} [Size α] [Size β] : Size (α × β) where size p := Size.size p.1 + Size.size p.2 3#eval usize ([1, 2], some 3) end Func
3

Example 4. Resolution chooses the instance from the type alone, which inferInstance makes explicit.

namespace Func inferInstance : Size (List )#check (inferInstance : Size (List )) end Func

Example 5. A class may give a field a default, which an instance may leave untouched or override.

namespace Func class Greet (α : Type) where label : String := "item" instance : Greet Bool where instance : Greet where label := "number" "item"#eval (Greet.label (α := Bool)) "number"#eval (Greet.label (α := )) end Func
"item"
"number"

Example 6. The of Lecture 2 is the method of the Membership class, resolved by the type of the container.

@Membership.mem : {α : outParam (Type u_1)} {γ : Type u_2} [self : Membership α γ] γ α Prop#check @Membership.mem

Example 7. Lean can build some instances automatically with deriving, here equality and a textual form for a finite type.

namespace Func inductive Coin where | heads | tails deriving Repr, DecidableEq Func.Coin.heads#eval Coin.heads end Func
Func.Coin.heads

Example 8. The derived equality lets decide settle a concrete disequality.

namespace Func example : Coin.heads Coin.tails := Coin.heads Coin.tails All goals completed! 🐙 end Func

Example 9. A class method carries an implicit instance argument, which #check displays.

namespace Func @Size.size : {α : Type} [self : Size α] α #check @Size.size end Func

Example 10. The associativity and commutativity that ac_rfl consulted in Lecture 4 are instances of Std.Associative and Std.Commutative.

@Std.Associative : {α : Sort u_1} (α α α) Prop#check @Std.Associative @Std.Commutative : {α : Sort u_1} (α α α) Prop#check @Std.Commutative