Skip to the content.
← Integers example Index Next: Accessing nested fields →

intRing is commutative, infinite, and — being $\mathbb{Z}$ itself — the initial ring (Chapter 7’s Theorem 1/2 hold for it as easily as possible, since it has essentially the “obvious” ring structure). This chapter’s matrix example shows a genuinely noncommutative ring. This section shows the other direction the axioms can specialize to: a commutative, finite ring, small enough to check every axiom by direct computation rather than by citing library lemmas.

The carrier: Fin 3

Fin 3 is Lean’s type of “naturals less than 3,” i.e. ${0, 1, 2}$. Its built-in +/* already wrap around modulo 3, so Fin 3’s arithmetic is $\mathbb{Z}/3\mathbb{Z}$’s arithmetic:

#eval (2 : Fin 3) + 2   -- 1   (2 + 2 = 4 ≡ 1 mod 3)
#eval (2 : Fin 3) * 2   -- 1   (2 * 2 = 4 ≡ 1 mod 3)

Building Group (Fin 3), CommGroup (Fin 3), Ring (Fin 3)

Because Fin 3 has only three elements, every one of Group/Ring’s axioms is a finite, decidable statement — literally “check this equation holds for all $3$ (or $3^2$, or $3^3$) choices of its variables.” This is exactly the kind of goal Chapter 12 recommends handing to decide rather than proving by hand:

def fin3Group : Group (Fin 3) where
  op := fun a b => a + b
  id := 0
  inv := fun a => -a
  assoc := by decide
  id_left := by decide
  id_right := by decide
  inv_left := by decide
  inv_right := by decide

def fin3CommGroup : CommGroup (Fin 3) where
  toGroup := fin3Group
  comm := by decide

def fin3Ring : Ring (Fin 3) where
  addGrp := fin3CommGroup
  mul := fun a b => a * b
  one := 1
  mul_assoc := by decide
  one_mul := by decide
  mul_one := by decide
  left_distrib := by decide
  right_distrib := by decide

Programmer’s corner (Python). by decide on a goal like assoc for Fin 3 is not magic. It is exactly what one would get from

domain = range(3)
all((a + b) + c == a + (b + c) for a in domain for b in domain for c in domain)

brute-force enumerate every combination of the (finitely many) variables and check the equation holds for each. The difference is not the algorithm, but where the check lives. all(...) is a runtime assertion: it only runs if some line of code calls it. If the call is omitted (or a later refactor deletes it), Python never notices that the property broke. decide instead runs as part of type-checking fin3Group itself. The definition does not elaborate at all unless the brute-force check succeeds, so there is no such thing as a fin3Group value that quietly skipped its axiom checks. It is the same enumeration, but promoted from “a test one hopes someone runs” to a condition the kernel enforces before the value can even exist.

#eval fin3Ring.addGrp.op 2 2          -- 1  (2 + 2 = 1 mod 3)
#eval fin3Ring.mul 2 2                 -- 1  (2 * 2 = 1 mod 3)
#eval fin3Ring.addGrp.toGroup.inv 1     -- 2  (-1 = 2 mod 3)

Compare this construction style directly with intRing (previous section) and mat2Ring (next section): intRing’s axioms needed Int.add_assoc-style library citations because Int is infinite. There is no way to “check all cases” of a statement about every integer. Fin 3’s axioms are statements about only finitely many (and concretely enumerable) elements, so they can instead be handled by exhaustive case-checking, which is exactly what decide automates. Recognizing which regime a goal is in — infinite carrier needing a real argument, versus finite carrier where brute enumeration is enough — is itself a useful instinct to build. See Chapter 12 for more on when decide is (and is not) the right tool.

Mathematical reading. fin3Ring is $\mathbb{Z}/3\mathbb{Z}$, the finite field with three elements. Only the Ring structure has been built here, though: a Field would additionally require every nonzero element to be invertible under multiplication, true for $\mathbb{Z}/3$ precisely because $3$ is prime, but this is not part of Ring’s axioms and not checked here. fin3Ring is the quotient $\mathbb{Z}/(3)$ by the ideal generated by $3$, with Fin 3’s built-in wraparound arithmetic giving reduction mod $3$ directly at the level of the representation, rather than through an explicit quotient construction.

Mathlib equivalent. Mathlib’s ZMod 3 is $\mathbb{Z}/3\mathbb{Z}$, already known to be a commutative ring (in fact a field, since $3$ is prime). There is no fin3Group/fin3CommGroup/fin3Ring bundle, and no decide calls needed to re-verify axioms already proved once, generically, for every n:

example : CommRing (ZMod 3) := inferInstance

#eval (2 : ZMod 3) + 2   -- 1
#eval (2 : ZMod 3) * 2   -- 1

The book’s by decide on fin3Ring’s fields brute-forces exactly the finitely many cases needed for this one carrier. Mathlib instead proves ZMod n’s ring axioms once for every n (through its construction as Fin n with wraparound arithmetic, combined with the general machinery for quotients of $\mathbb{Z}$), so ZMod 3’s instance costs nothing further to obtain.

Next

Continue to Accessing nested fields.


← Integers example Index Next: Accessing nested fields →
Try Lean
Lean playground · v1.4.18