Interactive Theorem Proving in Lean

Lean logo

Lecture 4: Algebraic structures.

Florent Schaffhauser
Heidelberg University

Interactive Theorem Proving in Lean - Lecture 4

Outline of the lectures

  • Lecture 1: An introduction to Lean.
  • Lecture 2: Tactics.
  • Lecture 3: Dependent types.
  • Lecture 4: Algebraic structures.
Interactive Theorem Proving in Lean - Lecture 4

Recap from Lecture 3

  • Besides simply-typed functions, we can define type formers, polymorphic functions and type families.
  • From a type family F : X → Type, we define an associated -type (the type of dependent pairs (x : X) × (F x)) and an associated -type (the type of dependent functions (x : X) → F x).
  • Given a predicate P : X → Prop, the proposition ∃ x : X, P x is the proposition whose proofs are constructed using dependent pairs (x : X) ×' (P x). And a proof of the proposition ∀ x : X, P x is a dependent function (x : X) → P x.
  • In this lecture, we will see how to formalize basic algebraic structures.
Interactive Theorem Proving in Lean - Lecture 4

Lecture 4 - Algebraic structures

  1. Semigroups.
  2. Monoids.
  3. Group project (pun intended).
Interactive Theorem Proving in Lean - Lecture 4

Practice files

In this lecture, we explore basic algebraic structures. The group project will be conducted in that direction. If you prefer to work on tactics and proofs, here are two suggestions:

  • The exercises from the logic file (Lecture 2).
  • The advanced tactics file (Lecture 3).

Practice File QR Code Logic. Practice File QR Code Advanced tactics.

Interactive Theorem Proving in Lean - Lecture 4

Semigroups

For convenience, we call a curried function of signature X → X → X an operation on X.

def Op (X : Type) : Type := XXX
  • We want to think of semigroups as dependent tuples consisting of a type X, an operation μ : Op X, and a proof that μ is associative.
  • More precisely, semigroups will be represented by a term of the form ⟨X, μ, a⟩ where a is a proof that the operation μ : Op X on the type X is associative, meaning that a is a term of type ∀ x y z : X, μ (μ x y) z = μ x (μ y z).
  • The point of having the associativity property of μ be a proposition is to guarantee that we can identify ⟨X, μ, a⟩ and ⟨X, μ, a'⟩ whenever a and a' are both proofs of the associativity property of μ.
Lecture 4 - Semigroups

Associativity

If X : Type and μ : Op X, we introduce the following predicate on Op X.

def Op.isAssociative {X : Type} (μ : Op X) : Prop :=
  ∀ x y z : X, μ (μ x y) z = μ x (μ y z)
  • Note the use of X as an implicit parameter here. This indeed makes Op.isAssociative a function of type signature Op X → Prop.
  • Since we have introduced a type Op X for each X, we can use dot notation and write μ.isAssociative instead of Op.isAssociative μ.
Lecture 4 - Semigroups

The type of semigroups

We can then define the type Semigroup as a structure (which we can think of for now as an inductive type with only one constructor).

structure Semigroup where
  carrier : Type
  op      : Op carrier
  assoc   : op.isAssociative
  • A semigroup S is a dependent triple with three components: S = ⟨S.carrier, S.op, S.assoc⟩. The term S.assoc is a proof of S.op.isAssociative.
  • Intuitively, we can think of the type of semigroups as a subtype of a -type:

Lecture 4 - Semigroups

Constructing semigroups

How do we construct a semigroup with "underlying magma" ⟨Nat, Nat.add⟩? By definition, we have to prove the associativity property of Nat.add : Nat → Nat → Nat.

def NatAddSemigroup : Semigroup where
  carrier := Nat            -- input by user
  op      := Nat.add        -- input by user
  assoc   := Nat.add_assoc  -- associativity proof, found by `exact?`
  • Using the keyword where helps to make things explicit (compared to using :=). After :=, you would need to write ⟨Nat, Nat.add, Nat.add_assoc⟩, which is in fact notation for Semigroup.mk Nat Nat.add Nat.add_assoc.
  • Note that we can use (· + ·) as notation for Nat.add.
  • In the group project, you will construct a semigroup with associated magma .
Lecture 4 - Semigroups

Neutral elements

If we want to say that an operation has a neutral element, we need an extra parameter, namely the element itself.

def Op.hasNeutral {X : Type} (μ : Op X) (e : X) : Prop :=
  ∀ x : X, μ e x = x ∧ μ x e = x

The expression NatAddSemigroup.op.hasNeutral (0 : Nat) now represents the property that (0 : Nat) is a neutral element for the operation Nat.add : Op Nat.

example : NatAddSemigroup.op.hasNeutral (0 : Nat) = ∀ n : Nat, 0 + n = n ∧ n + 0 = n := rfl

Note that NatAddSemigroup.op.hasNeutral (42 : Nat) also type-checks and that the type ∀ n : Nat, 42 + n = n ∧ n + 42 = n is well-formed but not inhabited.

Lecture 4 - Monoids

Monoids

Let us define monoids as semigroups equipped with an element and a proof that that element is neutral. We can do that by extending the Semigroup structure (giving us access to the carrier and op fields).

structure Monoid extends Semigroup where
  neutral_elt  : carrier
  neutral_ppty : op.hasNeutral neutral_elt
  • If we want to be more explicit, we can also write toSemigroup.carrier and toSemigroup.op.
  • With our definition, a monoid M is represented by a dependent tuple with five components M = ⟨X, μ, a, e, n⟩, where X : Type, μ : Op X, a is a proof of μ.isAssociative, e : X, and n is a proof of μ.hasNeutral e.
Lecture 4 - Monoids

Constructing monoids

In the project, you will be asked to construct a monoid with associated semigroup the term NatAddSemigroup constructed earlier. This will go as follows:

def NatAddZeroMonoid : Monoid where
  toSemigroup  := NatAddSemigroup
  neutral_elt  := sorry
  neutral_ppty := sorry
  • Here we started from the already constructed semigroup NatAddSemigroup. We could also fill out the fields carrier, op and assoc, of the Semigroup structure.
  • Note that we did not formalise the property μ.hasNeutral as ∃ e : X, ∀ x : X, (μ e x = x) ∧ (μ x e = x). Existential quantifiers must be treated with care.
Lecture 4 - Monoids

Property versus data

  • In general, it is good practice to separate property from data. If we has included the existential quantifier in the definition of a monoid, then it would not be clear how to access the neutral element, or if it is even possible to do so.
  • However, because of the added data, it is no longer clear that monoids form a subtype of the type of semigroups.
  • In the project, you will be asked to show that two monoids ⟨S, e, n⟩ and ⟨S', e', n'⟩ are equal if and only if S = S' as semigroups (the main reason for this is that if e and e' are neutral elements in the same semigroup S, then e = e', which we will prove formally in the project).
Lecture 4 - Monoids

Functions created automatically from a structure

From the Semigroup structure, we can project to the carrier, op and assoc fields of the structure.

#check Semigroup.assoc        -- Semigroup.assoc (self : Semigroup) : self.op.isAssociative

#check NatAddSemigroup.assoc  -- NatAddSemigroup.assoc : NatAdd.op.isAssociative

Similarly, from the Monoid structure, we get projections Monoid.neutral_elt and Monoid.neutral_ppty. We also get a function Monoid → Semigroup, which formalises the fact that the structure Monoid extends the structure Semigroup.

#check @Monoid.toSemigroup  -- Monoid.toSemigroup : Monoid → Semigroup
Lecture 4 - Monoids

Extended structures and coercions

  • We can extend the Semigroup structure to a structure CommSemigroup, with an additional field comm : op.isCommutative, where the predicate Op.isCommutative is defined by ∀ x y : X, μ x y = μ y x.
  • Given a function f : Semigroup → T and a term cS : CommSemigroup, we might then want the expression f cS to automatically type-check (without having to write f cS.toSemigroup). This can be achieved by implementing a coercion. The automatization is taken care of via a mechanism called type-class inference.
  • By specifying a function coe : CommSemigroup → Semigroup, our expression f cS will automatically be interpreted as f (coe cS).
instance : Coe CommSemigroup Semigroup where
  coe := CommSemigroup.toSemigroup
Lecture 4 - Monoids

Wrap-up

  • In Martin-Löf's typing system, the type of semigroups, monoids and other algebraic structures can be formalised using inductive types with one constructor.
  • It is useful to learn to distinguish property from data, in order to identify which -types are subtypes.
  • In practice, it is convenient to implement algebraic structures such as groups and rings as record types (structures, in Lean). This automatically gives us access to the various fields of the structure (meaning, the arguments of the constructor) via the appropriate projections.
  • Given a structure α and a structure β extending α, we can use the projection β → α as a coercion from β to α. Thanks to a type-class inference mechanism, functions defined on α then become automatically applicable on β.
Lecture 4 - Monoids

Group project

In the project file, you will be asked to:

  1. State and prove a theorem that says that, if μ : Op X is an operation that admits a neutral element, then such an element is unique.
  2. Formalise the definition of a commutative semigroup.
  3. Construct a commutative semigroup with associated semigroup NatAddSemigroup.
Lecture 4 - Group project

Project file

Here is the link to the Lean file for the group project:

Project File QR Code Group project.

Lecture 4 - Group project

--- ## Feedback on the workshop? And link to project file? Before we start working on the project files, I would like to ask for 5 minutes of your time, to give feedback on the workshop if you have not done it yet: ![Survey QR Code height:300](../../img/Feedback.svg) [Online survey](https://uni-heidelberg.evasys.de/evasys/online.php?p=WXQ8W) Thanks! :pray: :blush:

4. Formalise the definition of a group (by extending the `Monoid` structure). 5. Construct a group with underlying magma `⟨Int, (· + ·)⟩`. 6. Extend the definitions of monoids and groups to the commutative case and prove that `⟨Int, (· + ·)⟩` is a commutative group.