Skip to content

Instantly share code, notes, and snippets.

@TOTBWF
Last active December 28, 2021 21:53
Show Gist options
  • Select an option

  • Save TOTBWF/f938f3fe702e766f74bf97f3e4760508 to your computer and use it in GitHub Desktop.

Select an option

Save TOTBWF/f938f3fe702e766f74bf97f3e4760508 to your computer and use it in GitHub Desktop.
{-# OPTIONS --without-K --safe #-}
module InitialAlgebra where
open import Level hiding (zero) renaming (suc to β„“-suc)
open import Relation.Binary.PropositionalEquality.Core
open import Data.Nat.Base using (β„•; zero; suc; _+_)
open import Data.List.Base using (List; []; _∷_)
open import Categories.Category
open import Categories.Functor
open import Categories.Functor.Algebra
open import Categories.Category.Construction.F-Algebras
open import Categories.Object.Initial
open import Categories.Category.Instance.Sets
-- First, let us fix a category π’ž.
module Inductives {o β„“ e} (π’ž : Category o β„“ e) where
open Category π’ž
-- We can give semantics to an inductive type as initial
-- algebras for some endofunctor.
record Inductive : Set (o βŠ” β„“ βŠ” e) where
field
F : Endofunctor π’ž
Ο„ : F-Algebra F
initial : IsInitial (F-Algebras F) Ο„
open Functor F public
-- Let's rename some things to be a little more descriptive.
-- We use 'roll' here to indicate that the
-- F-Algebra Ο„ witnesses the fact that we can "roll up" a layer
-- of recursion.
open F-Algebra Ο„ renaming (A to Carrier; Ξ± to roll) public
-- Furthermore, we use 'fold' here to emphasize that the map between
-- F-Algebra carriers witnessed by 'initial' is indeed the fold we are looking for!
-- We have to do some silly things with modules here because of some annoying implicits.
open IsInitial initial public
module Fold Ξ± = F-Algebra-Morphism (IsInitial.! initial {Ξ±}) renaming (f to fold; commutes to fold-commutes)
open Fold public
open Inductive public
-- Now, for the heart of the matter! We are looking for a universal property
-- that says that some map out of an inductive "is a fold". Really, something
-- "is a fold" if it's an F-Algebra morphism out of the inductive type into
-- /some/ other F-Algebra.
-- By the uniqueness of the initial map, this means that our map 'f' is equal
-- to 'fold'.
record IsFold (T : Inductive) {A : Obj} (f : T .Carrier β‡’ A) : Set (β„“ βŠ” e) where
field
-- This morphism, combined with 'A', forms an F-Algebra.
alg : T .Fβ‚€ A β‡’ A
-- This condition is just restating the commuting condition from 'F-Algebra-Morphism'.
commutes : f ∘ T .roll β‰ˆ alg ∘ T .F₁ f
-- The F-Algebra in question.
Alg : F-Algebra (T .F)
Alg = record { A = A ; Ξ± = alg }
-- Bundle up the commuting condition along with the morphism on carriers to form an
-- F-Algebra morphism.
mor : F-Algebra-Morphism (T .Ο„) Alg
mor = record { f = f ; commutes = commutes }
-- Show that 'f' is indeed equal to a fold for some algebra.
is-fold : T .fold Alg β‰ˆ f
is-fold = T .!-unique mor
-- As an example, let's consider a concrete example: Lists.
private module Example where
-- Let's work over the Category of Sets.
open Category (Sets 0β„“)
open Inductives (Sets 0β„“)
-- First, let's define smash products.
data Smash (A B : Set) : Set where
nada : Smash A B
smash : A β†’ B β†’ Smash A B
-- Next, let's witness the fact that 'Smash' is functorial
-- in it's second argument.
smash-map : βˆ€ {A B C} β†’ (B β†’ C) β†’ Smash A B β†’ Smash A C
smash-map f nada = nada
smash-map f (smash a b) = smash a (f b)
SMASH : Set β†’ Endofunctor (Sets 0β„“)
Functor.Fβ‚€ (SMASH A) = Smash A
Functor.F₁ (SMASH A) = smash-map
Functor.identity (SMASH A) {x = nada} = refl
Functor.identity (SMASH A) {x = smash _ _} = refl
Functor.homomorphism (SMASH A) {x = nada} = refl
Functor.homomorphism (SMASH A) {x = smash _ _} = refl
Functor.F-resp-β‰ˆ (SMASH A) eq {x = nada} = refl
Functor.F-resp-β‰ˆ (SMASH A) eq {x = smash a _} = cong (smash a) eq
-- We can construct a Smash-Algebra whose carrier is List.
ListAlg : (A : Set) β†’ F-Algebra (SMASH A)
F-Algebra.A (ListAlg A) = List A
F-Algebra.Ξ± (ListAlg A) nada = []
F-Algebra.α (ListAlg A) (smash x xs) = x ∷ xs
open F-Algebra renaming (A to Carrier; α to ⟦_⟧)
open F-Algebra-Morphism renaming (f to mor)
-- Furthermore, given a Smash-Algebra, we can construct a map out of List
-- into it's carrier
list-fold : βˆ€ {A} β†’ (Ξ± : F-Algebra (SMASH A)) β†’ List A β†’ Ξ± .Carrier
list-fold α [] = ⟦ α ⟧ nada
list-fold α (x ∷ xs) = ⟦ α ⟧ (smash x (list-fold α xs))
-- Furthermore, list-fold commutes with it's associated algebra.
list-fold-commutes : βˆ€ {A} β†’ (Ξ± : F-Algebra (SMASH A)) β†’ list-fold Ξ± ∘ ⟦ ListAlg A ⟧ β‰ˆ ⟦ Ξ± ⟧ ∘ smash-map (list-fold Ξ±)
list-fold-commutes Ξ± {nada} = refl
list-fold-commutes Ξ± {smash x xs} = refl
-- This means that list-fold is an F-Algebra Morphism.
list-fold-mor : βˆ€ {A} β†’ (Ξ± : F-Algebra (SMASH A)) β†’ F-Algebra-Morphism (ListAlg A) Ξ±
mor (list-fold-mor Ξ±) = list-fold Ξ±
commutes (list-fold-mor Ξ±) = list-fold-commutes Ξ±
-- To make matters more interesting, 'list-fold-mor' witnesses initiality of 'ListAlg'.
list-fold-unique : βˆ€ {A} {Ξ± : F-Algebra (SMASH A)} β†’ (f : F-Algebra-Morphism (ListAlg A) Ξ±) β†’ F-Algebras (SMASH A) [ list-fold-mor Ξ± β‰ˆ f ]
list-fold-unique f {[]} = sym (f .commutes {x = nada})
list-fold-unique f {x ∷ xs} rewrite list-fold-unique f {xs} = sym (f .commutes {x = smash x xs})
ListAlgInitial : βˆ€ A β†’ IsInitial (F-Algebras (SMASH A)) (ListAlg A)
IsInitial.! (ListAlgInitial A) {Ξ±} = list-fold-mor Ξ±
IsInitial.!-unique (ListAlgInitial A) = list-fold-unique
-- All of this means that 'List' is an inductive type.
LIST : Set β†’ Inductive
Inductives.Inductive.F (LIST A) = SMASH A
Inductives.Inductive.Ο„ (LIST A) = ListAlg A
Inductives.Inductive.initial (LIST A) = ListAlgInitial A
-- With all of that categorical boilerplate out of the way, let's show that
-- 'length is a fold.
length : βˆ€ {A : Set} β†’ List A β†’ β„•
length [] = 0
length (_ ∷ xs) = suc (length xs)
-- First, we define the associated F-Algebra.
length-alg : βˆ€ {A} β†’ Smash A β„• β†’ β„•
length-alg nada = 0
length-alg (smash _ n) = suc n
-- Next, we prove the commuting condition.
length-commutes : βˆ€ {A} β†’ length ∘ (LIST A) .roll β‰ˆ (length-alg ∘ smash-map length)
length-commutes {x = nada} = refl
length-commutes {x = smash _ _} = refl
-- Then that's it!
length-is-fold : βˆ€ {A} β†’ IsFold (LIST A) length
Inductives.IsFold.alg length-is-fold = length-alg
Inductives.IsFold.commutes length-is-fold = length-commutes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment