Tom de Jong, 25—27 February 2026.
Moved from `gist` to this place on 17 April 2026.
Merged basic library into UF.Base on 3—4 June 2026.

This is the result of my own attempt to understand David Wärn's Agda
formalization [1] which proves that any type equipped with a binary operation
that is associative, commutative and idempotent must be a set, cf. Martin
Escardo's version [2].

[1] David Wärn. https://dwarn.se/agda/Idem.html, 17 February 2026.
    (See also https://mathstodon.xyz/deck/@dwarn/116091515645003634.)

[2] Martin Escardo. AlgebraicStructuresForcingSethood.Semilattices.lagda,
    23 February 2026.

As David pointed out to me, this proof is slightly different from his in that I
do not use commutativity of path composition to prove associativity of the
operation on loop spaces.

\begin{code}

{-# OPTIONS --safe --without-K #-}

module AlgebraicStructuresForcingSethood.Semilattices-streamlined where

\end{code}

To emphasize that we need very little for the below development we explicitly
list our minimal imports.

\begin{code}

open import MLTT.Universes
open import MLTT.Id
open import UF.Base using
  ( ap₂
  ; refl-left-neutral
  ; refl-right-neutral
  ; cancel-left
  ; conjugate-loop
  ; =-congr
  ; =-congr-refl
  ; =-congr-∙
  ; =-congr-∙'
  ; =-congr-nat
  ; =-congr-nat'
  )

\end{code}

We extract two criteria from [1] for the loop space to be trivial. The second
lemma is invoked at the very end.

\begin{code}

module pointed-type
        (A  : 𝓤 ̇ )
        (a₀ : A)
       where

 ΩA : 𝓤 ̇
 ΩA = a₀ = a₀

 trivial-Ω-endomap-criterion : (f : ΩA → ΩA)
                             → ((p : ΩA) → f (f p) = f p)
                             → ((p : ΩA) → f p ∙ f p = p)
                             → (p : ΩA) → p = refl
 trivial-Ω-endomap-criterion f f-idem f-self-concat p = III ⁻¹
  where
   I = f p ∙ f p         =⟨ ap₂ _∙_ ((f-idem p) ⁻¹) ((f-idem p) ⁻¹) ⟩
       f (f p) ∙ f (f p) =⟨ f-self-concat (f p) ⟩
       f p               =⟨ refl-right-neutral (f p) ⟩
       f p ∙ refl        ∎

   II : f p = refl
   II = cancel-left I

   III = refl        =⟨ refl ⟩
         refl ∙ refl =⟨ ap₂ _∙_ (II ⁻¹) (II ⁻¹) ⟩
         f p ∙ f p   =⟨ f-self-concat p ⟩
         p           ∎

\end{code}

The loop space is trivial if it can be equipped with a binary operation ⋆ such that
- it has an interchange law: (p ⋆ q) ∙ (r ⋆ s) = (p ∙ r) ⋆ (q ∙ s);
- it is idempotent, commutative and associative.

\begin{code}

 trivial-Ω-multiplication-criterion
  : (_⋆_ : ΩA → ΩA → ΩA)
  → ((p q r s : ΩA) → (p ⋆ q) ∙ (r ⋆ s) = (p ∙ r) ⋆ (q ∙ s))
  → ((p : ΩA) → p ⋆ p = p)
  → ((p q : ΩA) → p ⋆ q = q ⋆ p)
  → ((p q r : ΩA) → p ⋆ (q ⋆ r) = (p ⋆ q) ⋆ r)
  → (p : ΩA) → p = refl
 trivial-Ω-multiplication-criterion
  _⋆_ ⋆-interchange-∙ ⋆-idem ⋆-comm ⋆-assoc =
  trivial-Ω-endomap-criterion f f-idempotent f-self-concat-is-id
   where
    f : ΩA → ΩA
    f p = p ⋆ refl

    f-idempotent : (p : ΩA) → f (f p) = f p
    f-idempotent p =
     f (f p)           =⟨ refl ⟩
     (f p) ⋆ refl      =⟨ refl ⟩
     (p ⋆ refl) ⋆ refl =⟨ (⋆-assoc p refl refl) ⁻¹ ⟩
     p ⋆ (refl ⋆ refl) =⟨ ap (p ⋆_) (⋆-idem refl) ⟩
     p ⋆ refl          =⟨ refl ⟩
     f p               ∎

    f-self-concat-is-id : (p : ΩA) → f p ∙ f p = p
    f-self-concat-is-id p =
     f p ∙ f p               =⟨ refl ⟩
     (p ⋆ refl) ∙ (p ⋆ refl) =⟨ I ⟩
     (p ⋆ refl) ∙ (refl ⋆ p) =⟨ ⋆-interchange-∙ p refl refl p ⟩
     (p ∙ refl) ⋆ (refl ∙ p) =⟨ II ⟩
     p ⋆ p                   =⟨ ⋆-idem p ⟩
     p                       ∎
      where
       I  = ap ((p ⋆ refl) ∙_) (⋆-comm p refl)
       II = ap₂ _⋆_ (refl-right-neutral p) refl-left-neutral

\end{code}

Now suppose we have a type A with a binary operation _*_. The operation induces
an action on paths that we denote by _*_. Note that this * is not quite a
binary operation in the sense that it is not of type X → X → X for some X.

\begin{code}

module _
        (A   : 𝓤 ̇ )
        (_*_ : A → A → A)
       where

 _*_ : {a a' b b' : A} → a = a' → b = b' → a * b = a' * b'
 p * q = ap₂ _*_ p q

 *-interchange-∙ : {a a' b b' x y : A}
                    (p : a = a') (q : b = b') (r : a' = x) (s : b' = y)
                  → (p * q) ∙ (r * s) = (p ∙ r) * (q ∙ s)
 *-interchange-∙ refl refl refl refl = refl

\end{code}

We will need the following lemmas about applying * when one of its arguments is
of the form "eq-congr u v (p * q)". Of course, the point is to state these
lemmas in sufficient generality so that they follow trivially from path
induction.

\begin{code}

 *-=-congr-left : {a a' b b' c c' : A}
                    (u : a * b = c) (v : a' * b' = c')
                    (p : a = a') (q : b = b') (r : c = c')
                  → (=-congr u v (p * q)) * r
                    = =-congr (u * refl) (v * refl) ((p * q) * r)
 *-=-congr-left refl refl refl refl r = refl

 *-=-congr-right : {a a' b b' c c' : A}
                    (u : a * b = c) (v : a' * b' = c')
                    (p : c = c') (q : a = a') (r : b = b')
                  → p * (=-congr u v (q * r))
                    = =-congr (refl * u) (refl * v) (p * (q * r))
 *-=-congr-right refl refl p refl refl = refl

\end{code}

If * is commutative/associative, then so is * up to =-congr which is necessary
to make things type check as * is a dependent function.

\begin{code}

 commutativity-of-* : 𝓤 ̇
 commutativity-of-* = (a b : A) → a * b = b * a

 associativity-of-* : 𝓤 ̇
 associativity-of-* = (a b c : A) → (a * b) * c = a * (b * c)

 *-comm : (*-comm : commutativity-of-*)
           {a a' b b' : A}
           (p : a = a') (q : b = b')
         → p * q = =-congr (*-comm b a) (*-comm b' a') (q * p)
 *-comm *-comm refl refl = (=-congr-refl (*-comm _ _)) ⁻¹

\end{code}

One may expect associativity to go from (p * q) * r to p * (q * r) in line
with associativity-of-*, but we go with the following to avoid having to invert
paths.

\begin{code}

 *-assoc
  : (*-assoc : associativity-of-*)
    {a a' b b' c c' : A}
    (p : a = a') (q : b = b') (r : c = c')
  → p * (q * r) = =-congr (*-assoc a b c) (*-assoc a' b' c') ((p * q) * r)
 *-assoc *-assoc refl refl refl = (=-congr-refl (*-assoc _ _ _)) ⁻¹

\end{code}

If * is idempotent, then we can define an operation *' on paths from a to b.
This operation is again idempotent, and it is crucial here that we have not
restricted to loops yet, so that this has a (trivial) proof by path induction.

\begin{code}

 idempotentency-of-* : 𝓤 ̇
 idempotentency-of-* = (a : A) → a * a = a

 module idempotent
         (*-idem : idempotentency-of-*)
        where

  _*'_ : {a b : A} → a = b → a = b → a = b
  _*'_ {a} {b} p q = =-congr (*-idem a) (*-idem b) (p * q)

  *'-idempotent : {a b : A} (p : a = b) → p *' p = p
  *'-idempotent refl = =-congr-refl (*-idem _)

\end{code}

We now assume that A has a point a₀ and we restrict *' to loops on a₀, denoting
this operation by *Ω.

\begin{code}

  module pointed
          (a₀ : A)
         where

   open pointed-type A a₀

   private
    ι : a₀ * a₀ = a₀
    ι = *-idem a₀

   _*Ω_ : ΩA → ΩA → ΩA
   _*Ω_ = _*'_

   NB : (p q : ΩA) → p *Ω q = conjugate-loop ι (p * q)
   NB p q = refl

   *Ω-idempotent : (p : ΩA) → p *Ω p = p
   *Ω-idempotent = *'-idempotent

   *Ω-refl : refl *Ω refl = refl
   *Ω-refl = =-congr-refl ι

   *Ω-interchange-∙ : (p q r s : ΩA)
                    → (p *Ω q) ∙ (r *Ω s) = (p ∙ r) *Ω (q ∙ s)
   *Ω-interchange-∙ p q r s =
    (p *Ω q) ∙ (r *Ω s)                                 =⟨ refl ⟩
    conjugate-loop ι (p * q) ∙ conjugate-loop ι (r * s) =⟨ I    ⟩
    conjugate-loop ι ((p * q) ∙ (r * s))                =⟨ II   ⟩
    conjugate-loop ι ((p ∙ r) * (q ∙ s))                 =⟨ refl ⟩
    (p ∙ r) *Ω (q ∙ s)                                   ∎
     where
      I  = (=-congr-∙ ι ι ι (p * q) (r * s)) ⁻¹
      II = ap (conjugate-loop ι) (*-interchange-∙ p q r s)

\end{code}

Assuming that * is commutative, we wish to show that *Ω is commutative.
The strategy for this is to
  (1) prove that this holds up to conjugating by some appropriately chosen
      (i.e. reverse engineered) loop γ;
  (2) using idempotency of *Ω, prove that conjugating by γ is the identity;
  (3) conclude that *Ω is commutative (with no conjugation).

\begin{code}

   module _
           (*-comm : commutativity-of-*)
          where

    γ : a₀ = a₀
    γ = conjugate-loop ι (*-comm a₀ a₀)

    *Ω-commutative-up-to-conjugation : (p q : ΩA)
                                      → p *Ω q = conjugate-loop γ (q *Ω p)
    *Ω-commutative-up-to-conjugation p q =
     p *Ω q                                      =⟨ refl ⟩
     conjugate-loop ι (p * q)                    =⟨ I    ⟩
     conjugate-loop ι (=-congr γ₀ γ₀ (q * p))   =⟨ refl ⟩
     =-congr ι ι (=-congr γ₀ γ₀ (q * p))       =⟨ II   ⟩
     =-congr (=-congr ι ι γ₀)
              (=-congr ι ι γ₀)
              (=-congr ι ι (q * p))             =⟨ refl ⟩
     =-congr γ γ (=-congr ι ι (q * p))         =⟨ refl ⟩
     conjugate-loop γ (conjugate-loop ι (q * p)) =⟨ refl ⟩
     conjugate-loop γ (q *Ω p)                   ∎
      where
       γ₀ : a₀ * a₀ = a₀ * a₀
       γ₀ = *-comm a₀ a₀
       I  = ap (conjugate-loop ι) (*-comm *-comm p q)
       II = =-congr-nat γ₀ γ₀ ι ι (q * p)

    conjugate-loop-comm-is-id : (p : ΩA) → conjugate-loop γ p = p
    conjugate-loop-comm-is-id p =
     conjugate-loop γ p          =⟨ I  ⟩
      conjugate-loop γ (p *Ω p) =⟨ II ⟩
      p *Ω p                    =⟨ *Ω-idempotent p ⟩
      p                          ∎
       where
        I  = ap (conjugate-loop γ) ((*Ω-idempotent p) ⁻¹)
        II = (*Ω-commutative-up-to-conjugation p p) ⁻¹

    *Ω-commutative : (p q : ΩA) → p *Ω q = q *Ω p
    *Ω-commutative p q =
      p *Ω q                    =⟨ *Ω-commutative-up-to-conjugation p q ⟩
      conjugate-loop γ (q *Ω p) =⟨ conjugate-loop-comm-is-id (q *Ω p) ⟩
      (q *Ω p)                  ∎

\end{code}

Assuming that * is associative, we wish to show that *Ω is associative.
The strategy for this is like for commutativity above:
  (1) prove that *Ω is associative up to conjugating by some appropriately
      chosen (i.e. reverse engineered) loop α;
  (2) using idempotency of *Ω, prove that conjugating by α is the identity;
  (3) conclude that *Ω is commutative (with no conjugation).

Compared the proof for commutativity, there are two more steps here, namely to
show that (p *Ω q) *Ω r can be expressed as ((p * q) * r) up to conjugation,
and similarly for the other bracketing.

\begin{code}

   module _
           (*-assoc : associativity-of-*)
          where

    ι₁ : (a₀ * a₀) * a₀ = a₀
    ι₁ = (ι * refl) ∙ ι

    ι₂ : a₀ * (a₀ * a₀) = a₀
    ι₂ = (refl * ι) ∙ ι

    *Ω-is-*-up-to-conjugation₁
     : (p q r : ΩA)
     → (p *Ω q) *Ω r = conjugate-loop ι₁ ((p * q) * r)
    *Ω-is-*-up-to-conjugation₁ p q r =
     (p *Ω q) *Ω r                                               =⟨ refl ⟩
     conjugate-loop ι (conjugate-loop ι (p * q) * r)             =⟨ I    ⟩
     conjugate-loop ι (conjugate-loop (ι * refl) ((p * q) * r)) =⟨ II   ⟩
     conjugate-loop ((ι * refl) ∙ ι) ((p * q) * r)              =⟨ refl ⟩
     conjugate-loop ι₁ ((p * q) * r)                             ∎
      where
       I  = ap (conjugate-loop ι) (*-=-congr-left ι ι p q r)
       II = (=-congr-∙' (ι * refl) ι (ι * refl) ι ((p * q) * r)) ⁻¹

    *Ω-is-*-up-to-conjugation₂
     : (p q r : ΩA)
     → p *Ω (q *Ω r) = conjugate-loop ι₂ (p * (q * r))
    *Ω-is-*-up-to-conjugation₂ p q r =
     p *Ω (q *Ω r)                                               =⟨ refl ⟩
     conjugate-loop ι (p * conjugate-loop ι (q * r))             =⟨ I    ⟩
     conjugate-loop ι (conjugate-loop (refl * ι) (p * (q * r))) =⟨ II   ⟩
     conjugate-loop ((refl * ι) ∙ ι) (p * (q * r))              =⟨ refl ⟩
     conjugate-loop ι₂ (p * (q * r))                             ∎
      where
       I  = ap (conjugate-loop ι) (*-=-congr-right ι ι p q r)
       II = (=-congr-∙' (refl * ι) ι (refl * ι) ι (p * (q * r))) ⁻¹

    α : a₀ = a₀
    α = =-congr ι₁ ι₂ (*-assoc a₀ a₀ a₀)

\end{code}

The rebracketing convention follows that of *-assoc.

\begin{code}

    *Ω-associative-up-to-conjugation
     : (p q r : ΩA)
     → p *Ω (q *Ω r) = conjugate-loop α ((p *Ω q) *Ω r)
    *Ω-associative-up-to-conjugation p q r =
     p *Ω (q *Ω r)                                       =⟨ I    ⟩
     conjugate-loop ι₂ (p * (q * r))                     =⟨ II   ⟩
     conjugate-loop ι₂ (conjugate-loop α₀ ((p * q) * r)) =⟨ refl ⟩
     =-congr ι₂ ι₂ (=-congr α₀ α₀ ((p * q) * r))       =⟨ III  ⟩
     =-congr (=-congr ι₁ ι₂ α₀)
              (=-congr ι₁ ι₂ α₀)
              (=-congr ι₁ ι₁ ((p * q) * r))             =⟨ refl ⟩
     =-congr α α (=-congr ι₁ ι₁ ((p * q) * r))         =⟨ refl ⟩
     conjugate-loop α (conjugate-loop ι₁ ((p * q) * r))  =⟨ IV   ⟩
     conjugate-loop α ((p *Ω q) *Ω r)                    ∎
      where
       α₀ = *-assoc a₀ a₀ a₀
       I = *Ω-is-*-up-to-conjugation₂ p q r
       II = ap (conjugate-loop ι₂) (*-assoc *-assoc p q r)
       III = =-congr-nat' α₀ ι₁ ι₂ ((p * q) * r)
       IV = ap (conjugate-loop α) ((*Ω-is-*-up-to-conjugation₁ p q r) ⁻¹)

    conjugate-loop-assoc-is-id : (p : ΩA) → conjugate-loop α p = p
    conjugate-loop-assoc-is-id p =
     conjugate-loop α p                 =⟨ I  ⟩
     conjugate-loop α (p *Ω p)         =⟨ II ⟩
     conjugate-loop α ((p *Ω p) *Ω p) =⟨ III ⟩
     p *Ω (p *Ω p)                    =⟨ IV ⟩
     p *Ω p                            =⟨ V ⟩
     p                                  ∎
      where
       I   = ap (conjugate-loop α) ((*Ω-idempotent p) ⁻¹)
       II  = ap (λ - → conjugate-loop α (- *Ω p)) ((*Ω-idempotent p) ⁻¹)
       III = (*Ω-associative-up-to-conjugation p p p) ⁻¹
       IV  = ap (p *Ω_) (*Ω-idempotent p)
       V   = *Ω-idempotent p

    *Ω-associative : (p q r : ΩA) → p *Ω (q *Ω r) = (p *Ω q) *Ω r
    *Ω-associative p q r =
     p *Ω (q *Ω r)                    =⟨ I  ⟩
     conjugate-loop α ((p *Ω q) *Ω r) =⟨ II ⟩
     ((p *Ω q) *Ω r)                  ∎
      where
       I  = *Ω-associative-up-to-conjugation p q r
       II = conjugate-loop-assoc-is-id ((p *Ω q) *Ω r)

\end{code}

To recap: given an idempotent, commutative and associative operation * on a
pointed type A, we obtained an idempotent, commutative and associative operation
*Ω on ΩA. Moreover, *Ω satisfies the interchange law, so that we can prove the
result claimed at the top by the trivial-Ω-multiplication-criterion lemma.

\begin{code}

 module _
         (a₀ : A)
         (*-idem  : idempotentency-of-*)
         (*-comm  : commutativity-of-*)
         (*-assoc : associativity-of-*)
        where

  open pointed-type A a₀
  open idempotent *-idem
  open pointed a₀

  ΩA-is-trivial : (p : ΩA) → p = refl
  ΩA-is-trivial = trivial-Ω-multiplication-criterion
                   _*Ω_
                   *Ω-interchange-∙
                   *Ω-idempotent
                   (*Ω-commutative *-comm)
                   (*Ω-associative *-assoc)

\end{code}