Intro
{-# OPTIONS --allow-unsolved-metas --without-K #-}
Curry-Howard correspondence
Proof rules and the rules of type systems correspond to each other:
| Logic | Programs |
|---|---|
| cf. GLoIn | cf. ThProg |
| Logical formulas A ∧ B → A | Types of programs A × B → A |
| Proofs Γ ⊢ A ∧ B → A | Typing: Γ ⊢ (λ p. p λ x y. x) : A × B → A |
| Implication A → B | Function type A → B |
| Conjunction A ∧ B | Product type A × B |
| Disjunction A ∨ B | Union type A + B |
| Universal quantifier ∀x.φ(x) | Polymorphism ∀ x.φ(x) |
⇒ We prove mathematical statements by constructing programs of the appropriate type.
Syntax
- Haskell-like
- Identifiers: UTF-8 character strings separated by whitespace and
.;{}()@"
Function types
Agda is dependently typed, which means that types can depend on values. Since 'Set' is itself a type, this lets us implement polymorphism:
id : ∀ (S : Set) → S → S id S x = x
Here S is a value of type Set.
id' : ∀ (S : Set) → (S → S) id' = λ S → λ x → x
The function type S → S itself also lives in Set:
Endofunction : Set → Set Endofunction S = S → S
-- Definition using Endofunction: id₂ : ∀ (S : Set) → Endofunction S id₂ S x = x
Holes
Instead of top to bottom, one writes Agda programs from the outside in:
id₃ : ∀ (S : Set) → Endofunction S id₃ S x = x -- Replace x by ? to create a hole
Operatoren
_∨_ : Set → Set → Set₁ A ∨ B = ∀ (C : Set) → (A → C) → (B → C) → C _∧_ : Set → Set → Set₁ A ∧ B = ∀ (C : Set) → (A → B → C) → C
- If a function X -> Y has X and Y at level ℓ, then (X → Y) is at level ℓ too
- A function X -> Y means: ∀ (x : X) → Y
- The parameter C is of type Set
- => Set → Set ∈ Set₁
Modules
module Logic-Examples (A : Set) (B : Set) where ∨-comm : (A ∨ B) → (B ∨ A) ∨-comm A∨B C B→C A→C = A∨B C A→C B→C
Data types
data ℕ : Set where zero : ℕ suc : ℕ → ℕ _+_ : ℕ → ℕ → ℕ zero + b = b suc a + b = suc (a + b) open import Data.Bool using (Bool; true; false) _≡?_ : ℕ → ℕ → Bool zero ≡? zero = true zero ≡? suc b = false suc a ≡? zero = false suc a ≡? suc b = a ≡? b data _≡_ {ℓ} {X : Set ℓ} : X → X → Set ℓ where refl : ∀ (x : X) → x ≡ x infix 4 _≡_ zero+a≡a : ∀ (a : ℕ) → zero + a ≡ a zero+a≡a a = refl a
[End of lecture on 13 April 2026]
Implicit arguments
- Function arguments can be omitted if they are determined automatically by unification.
- Syntax:
{…}to mark an argument as implicit in a function definition_to let Agda figure out an explicit argument at a call site.
-- Functions preserve equality: cong : ∀ {a b : ℕ} → (f : ℕ → ℕ) → a ≡ b → f a ≡ f b cong f (refl a) = refl (f a) -- When we call cong, the values of a and b follow -- automatically from `a ≡ b` a+zero≡a : ∀ (a : ℕ) → a + zero ≡ a a+zero≡a zero = refl zero a+zero≡a (suc a) = cong {a + zero} {a} suc (a+zero≡a a) -- identical to: cong {a + zero} {a} suc (a+zero≡a a)
Another example: the implicit carrier set in _≡_ above.
In the stdlib the parameter of refl is implicit, since it is usually determined
by the context, as for instance in the definition of sym:
sym : ∀ {X : Set} {a : X} {b} → a ≡ b → b ≡ a sym (refl _) = refl _ -- ^ ^--- Agda infers the parameter from the context -- '---- The value gets no name, but is still available: -- a : ℕ (not in scope) trans : ∀ {X : Set} {a b c : X} → a ≡ b → b ≡ c → a ≡ c trans (refl x) eq = eq +-suc : ∀ a b → suc (a + b) ≡ (a + suc b) +-suc zero b = refl (suc b) +-suc (suc a) b = cong suc (+-suc a b) +-comm₀ : ∀ a b → a + b ≡ b + a +-comm₀ zero b = sym (a+zero≡a b) +-comm₀ (suc a) b = trans (cong suc IH) (+-suc b a) where IH = +-comm₀ a b subst : ∀ {a b : ℕ} → (P : ℕ → Set) → a ≡ b → P a → P b subst P (refl a) = id (P a) +-comm : ∀ a b → _≡_ (a + b) (b + a) +-comm zero b = sym (a+zero≡a b) +-comm (suc a) b = trans (cong suc (+-comm a b)) (+-suc b a)
Equational Reasoning
_∎ : ∀ {X : Set} (n : X) → n ≡ n _∎ = refl infix 3 _∎ _≡⟨_⟩_ : ∀ {X : Set} n {m} → n ≡ m → {ℓ : X} → m ≡ ℓ → n ≡ ℓ n ≡⟨ n≡m ⟩ m≡ℓ = trans n≡m m≡ℓ infixr 2 _≡⟨_⟩_ +-comm' : ∀ a b → _≡_ (a + b) (b + a) +-comm' zero b = sym (a+zero≡a b) +-comm' (suc a) b = (suc a + b) ≡⟨ cong suc (+-comm' a b) ⟩ suc (b + a) ≡⟨ +-suc b a ⟩ (b + suc a) ∎
Warning: the equational chains in the stdlib start with begin_
Decision procedures
- Embedding Bool ↪ Set
- Correctness proof for
_≡?_ - Dot patterns
-- import Data.Empty data ⊥ : Set where -- import Data.Unit data ⊤ : Set where tt : ⊤ Truth : Bool → Set Truth false = ⊥ Truth true = ⊤ ≡?-⇐ : ∀ a b → a ≡ b → Truth (a ≡? b) ≡?-⇐ zero a (refl a) = tt ≡?-⇐ (suc a) (suc a) (refl (suc a)) = ≡?-⇐ a a (refl _)
[End of lecture on 16 April 2026]
In ≡?-⇐ we use the principle of "forcing".
Converse direction:
≡?-⇒ : ∀ a b → Truth (a ≡? b) → a ≡ b ≡?-⇒ zero zero t = refl _ ≡?-⇒ zero (suc b) () -- no '=' here, () ≡ "impossible pattern" ≡?-⇒ (suc a) (suc b) t = cong suc (≡?-⇒ a b t)
Level
import Level as L open import Level using (Level) weak-initial : {ℓ : Level} (X : Set ℓ) → Set (L.suc ℓ) weak-initial {ℓ} X = ∀ (Y : Set ℓ) → (X → Y)
Functions
_∘_ : ∀ {ℓ₁ ℓ₂ ℓ₃} {X : Set ℓ₁} {Y : Set ℓ₂} {Z : Set ℓ₃} → (Y → Z) → (X → Y) → (X → Z) (g ∘ f) x = g (f x) ∘-assoc : ∀ {ℓ₁ ℓ₂ ℓ₃ ℓ₄} {W : Set ℓ₁} {X : Set ℓ₂} {Y : Set ℓ₃} {Z : Set ℓ₄} (f : W → X) (g : X → Y) (h : Y → Z) → h ∘ (g ∘ f) ≡ (h ∘ g) ∘ f ∘-assoc f g h = refl _
Records
module Non-Dependent-Product where
The usual product is a record:
record _×_ {ℓ ℓ' : Level} (X : Set ℓ) (Y : Set ℓ') : Set (ℓ L.⊔ ℓ') where constructor _,_ field fst : X snd : Y module ×-properties {ℓ ℓ' : Level} (X : Set ℓ) (Y : Set ℓ') where swap : X × Y → Y × X swap (x , y) = y , x swap' : X × Y → Y × X swap' p = record { fst = p.snd ; snd = p.fst } where module p = _×_ p
Dependent-Product:
record Σ {ℓ ℓ' : Level} (X : Set ℓ) (P : X → Set ℓ') : Set (ℓ L.⊔ ℓ') where constructor _,_ field fst : X snd : P fst syntax Σ A (λ x → B) = Σ[ x ∈ A ] B _×_ : {ℓ ℓ' : Level} (X : Set ℓ) (Y : Set ℓ') → Set (ℓ Level.⊔ ℓ') X × Y = Σ X (λ x → Y) ∃ : {ℓ ℓ' : Level} {X : Set ℓ} (P : X → Set ℓ') → Set (ℓ L.⊔ ℓ') ∃ = Σ _ syntax ∃ (λ x → B) = ∃[ x ] B
Currying the dependent product = dependent functions:
module Dependent-Currying (X : Set) (P : X → Set) (Y : Set) where curry : (Σ[ x ∈ X ] (P x) → Y) → (∀ (x : X) → P x → Y) curry f x Px = f (x , Px) uncurry : (∀ (x : X) → P x → Y) → (Σ[ x ∈ X ] (P x) → Y) uncurry f (x , Px) = f x Px _⇔_ : Set → Set → Set X ⇔ Y = (X → Y) × (Y → X) [un]curry : (∀ (x : X) → P x → Y) ⇔ (∃[ x ] (P x) → Y) [un]curry = uncurry , curry uncurry∘curry≡id : uncurry ∘ curry ≡ id _ uncurry∘curry≡id = refl (uncurry ∘ curry) curry∘uncurry≡id : curry ∘ uncurry ≡ id (∀ (x : X) → P x → Y) curry∘uncurry≡id = refl _
Functions
The relation f ≐ g expresses that the functions f and g are pointwise
identical:
_≐_ : ∀ {ℓ₁ ℓ₂} {X : Set ℓ₁} {Y : Set ℓ₂} (f g : X → Y) → Set (ℓ₁ L.⊔ ℓ₂) f ≐ g = ∀ x → f x ≡ g x
Stdlib
import Function.Base using (_∘_) -- ∘ allows composition of dependent functions