Skip to the content.
← Index Next: Universes →

Every algebraic definition in this book — Group, Ring, Module — is written as a plain structure. Mathlib, the real Lean library, defines all of these using class instead:

-- this book's style
structure Group (G : Type) where
  op : G  G  G
  id : G
  inv : G  G
  assoc :  a b c : G, op (op a b) c = op a (op b c)
  id_left :  a : G, op id a = a
  -- ...

-- Mathlib's style (schematically — not the literal Mathlib source)
class Group (G : Type) where
  op : G  G  G
  id : G
  inv : G  G
  assoc :  a b c : G, op (op a b) c = op a (op b c)
  id_left :  a : G, op id a = a
  -- ...

Syntactically these look almost identical. class is very nearly a shorthand for structure plus one extra mechanism. Understanding exactly what that mechanism is, and why this book avoids it until the intuition class would otherwise hide has been built, is the point of this section.

A class is a structure whose inhabitants Lean is willing to search for automatically, through a process called typeclass resolution (or “instance search”). When you write class Group (G : Type), and then register a particular group structure with the keyword instance instead of def:

instance : Group Int where
  op := fun a b => a + b
  id := 0
  inv := fun a => -a
  -- ...

Lean records Int’s group structure in a global table. From then on, any function that takes an implicit [Group G] argument (square brackets, not curly braces — this is a third kind of argument, an instance argument) has it filled in automatically whenever G is unified with Int, with no explicit term needed at the call site:

def opTwice [Group G] (x : G) : G :=
  Group.op x x   -- the `Group G` instance is found automatically

#eval opTwice (3 : Int)   -- Lean infers the `Group Int` instance silently

Compare this to the style used throughout this book, where every theorem about groups takes an explicit term Grp : Group G as a genuine function argument:

def opTwice (Grp : Group G) (x : G) : G :=
  Grp.op x x

#eval opTwice intGroup 3   -- the specific Group term must be named

Mathematically, the difference is this: class + instance implements the same convention as ordinary mathematical prose, in which one writes “let $G$ be a group” once and then simply writes $a \cdot b$, $e$, $a^{-1}$ thereafter. The specific group structure is left implicit, tracked by context, and never re-named at each use. Lean’s typeclass mechanism automates exactly that convention. A structure-only approach (this book’s style) requires the structure to always be carried around explicitly, so the group operation is never left unstated.

Why this book deliberately avoids class, for now

class’s automation is exactly what would make it harder to see, the first time through, what is actually happening. Every Grp.op, every Grp.assoc in this book’s proofs is a visible, traceable term. One can always ask “where did this fact come from?” and point at the specific argument it is a field of. With class, that same information is present but resolved silently by the elaborator — convenient once trusted, but difficult to debug the first time instance resolution picks the “wrong” (or an unexpected) instance, or fails to find one at all. Reading the explicit version first, this book’s approach, builds the mental model that makes debugging silent instance-resolution failures manageable later, since the data being threaded through remains known even once no longer visible in the source text.

The bridge to Mathlib

Nothing about the mathematical content changes between the two styles. Group’s axioms are the axioms, in either encoding. The type-class version also usually bundles in the group’s *, ⁻¹, 1 as genuine notation (through further, separate mechanisms — Mul, Inv, One type classes that Group extends), so that Mathlib code reads as a * b instead of Grp.op a b. Chapter 13’s suggested projects include redoing this book’s Group/Ring as type classes once the reader is comfortable doing so. At that point, everything in this section is the vocabulary needed.

A note on .toGroup, .toPoint, and coercion

Whenever a structure extends another (Chapter 2’s Point3D extends Point, this book’s CommGroup extends Group), Lean generates a field named .toX that projects back to the parent structure. cg.toGroup recovers the underlying Group from a CommGroup cg. Lean also permits dropping the .toGroup and writing cg.op directly, silently inserting the projection. This automatic insertion of a “translate from one type to another” function is an instance of coercion, the general mechanism by which Lean lets a term of one type stand in for a term of a related type without the conversion being written explicitly. It is the same general idea, though a much simpler case, as coercing Nat values into Int. The literal projection differs, but “insert a conversion function automatically so notation reads naturally” is the shared mechanism.

Read more: TPiL’s chapters on “Structures and Records” and “Type Classes” cover both topics in this section directly, including coercions, in more depth and with additional worked examples than this book provides.


← Index Next: Universes →
Try Lean
Lean playground · v1.4.18