---
layout: default
title : "Overture.Cayley module (The Agda Universal Algebra Library)"
date : "2026-05-31"
author: "the agda-algebras development team"
---
### Cayley tables for finite operations
This is the [Overture.Cayley][] module of the [Agda Universal Algebra Library][].
A *Cayley table* (or *multiplication table*) specifies a binary operation on a finite
set by tabulating every product. This module fixes a small, reusable representation
for finite operations together with the decision procedures that discharge their
algebraic laws, and so answers the following practical question:
> How does one represent a binary operation on a finite set by its Cayley table in
> Agda, and how does one prove the laws of such an operation?
The representation deliberately depends only on the finite-data parts of the standard
library — `Fin`{.AgdaDatatype}, `Vec`{.AgdaDatatype}, and decidable equality on
`Fin n` — and does not depend on the standard library's algebra hierarchy.
A binary operation is just a function `Fin n → Fin n → Fin n`; we do not route it
through `Algebra.Core.Op₂` or any bundle. The representation is a square table of
indices. The carrier is the canonical `n`-element type `Fin n`, and a Cayley table
is an `n × n` array of elements of `Fin n`, stored row-major as a
`Vec`{.AgdaDatatype} of rows:
> `⟦ t ⟧ a b` reads the entry in row `a`, column `b`. This makes the table itself a
> first-class, inspectable datum — the literal you write down *is* the Cayley table
> — while `⟦_⟧`{.AgdaFunction} turns it into the binary operation in
> `Fin n → Fin n → Fin n` that the `Classical`{.AgdaModule} builders
> (`opsToMagma`{.AgdaFunction}, `eqsToGroup`{.AgdaFunction}, …) consume.
The pay-off is that an equational law over a finite carrier is a *decidable*
proposition: `Fin n` has decidable equality, and `Data.Fin.Properties.all?` turns a
pointwise decision procedure into a decision for a universally quantified statement.
So each law (associativity, commutativity, idempotence, the identity and inverse
laws) is discharged by `from-yes`{.AgdaFunction} applied to the corresponding
decision, with no hand-written case dump.
Equivalently, if the table you wrote down does *not* satisfy a law, the decision
reduces to `no`{.AgdaInductiveConstructor} and the `from-yes`{.AgdaFunction} term
fails to type-check; in this way, the operation's properties can be checked by the
Agda type checker.
<!--
```agda
{-# OPTIONS --cubical-compatible --exact-split --safe #-}
module Overture.Cayley where
open import Agda.Primitive using () renaming ( Set to Type )
open import Data.Nat using ( ℕ )
open import Data.Fin using ( Fin )
open import Data.Vec.Base using ( Vec ; lookup )
open import Relation.Nullary.Decidable.Core using ( from-yes ) public
```
-->
#### The table representation
```agda
Table : ℕ → Type
Table n = Vec (Vec (Fin n) n) n
⟦_⟧ : ∀ {n} → Table n → (Fin n → Fin n → Fin n)
⟦ t ⟧ a b = lookup (lookup t a) b
```
#### Decidable algebraic laws
The decidable equational-law checkers for finite operations — `Associative?`,
`Commutative?`, `Idempotent?`, the identity/inverse laws, and the two-operation
absorption and distributivity laws — live in [`Overture.Operations.Properties`][Overture.Operations.Properties].
They take an arbitrary finite operation `Fin n → Fin n → Fin n` and do not touch the
table representation, so they are not specific to Cayley tables; finiteness is simply
what makes the laws decidable. Together with the `Table`/`⟦_⟧` machinery here and
the re-exported `from-yes`{.AgdaFunction}, they let a finite example discharge its
defining equations without writing a single case by hand.
#### A note on the operation type
This module types a binary operation as a bare `Fin n → Fin n → Fin n` rather than
the library's tuple-indexed `Op`{.AgdaFunction}. There is a single canonical,
arity-first `Op I A = (I → A) → A` in `Overture.Operations`{.AgdaModule}; although
that tuple-indexed shape is the right one for the universal-algebra meta-theory, it
is the wrong shape for a Cayley table. A table is read in *curried* form (`⟦ t ⟧ a
b` is the row-`a`, column-`b` entry) and the `Classical`{.AgdaModule} builders
(`opsToMagma`{.AgdaFunction}, `eqsToGroup`{.AgdaFunction}, …) that consume
`⟦_⟧`{.AgdaFunction} take their binary operation curried as `Fin n → Fin n → Fin n`.
Routing `⟦_⟧`{.AgdaFunction} through the tuple-indexed `Op (Fin 2) (Fin n)` would
only insert `Curry₂`/`Uncurry₂` adapters at every call site and obscure the
two-dimensional table, so the finite Cayley-table case keeps the plain curried
function type. The decidable-law checkers in [`Overture.Operations.Properties`][Overture.Operations.Properties]
take the same bare `Fin n → Fin n → Fin n`, for the same reason: finiteness is what
makes the laws decidable, independently of how an operation's arguments are packaged.