module Realisability.PCA.Combinatorial where
Combinatorial bases for PCAs🔗
private variable
: Level
ℓ : Type ℓ
A : Nat n
Traditional introductions to realisability (e.g. de Jong’s (2024) or Bauer’s (2022)) introduce partial combinatory algebras in terms of combinators, elements and satisfying the rules and Unfortunately, working with this notion of pca in Agda is very inconvenient:
To start with, we have to explicitly write out and the requirements that the constants and are defined; and that the application is defined when is defined, and similarly for and
Because we generally write programs using functional notation, it is important that the normal form of a program like is compact, both for performance reasons and so that we can actually work with them. However, the normal forms of programs in terms of combinators are huge monsters.
On a more technical point, it’s good for type inference if, when working against an abstract PCA we have that the application function
_%_
and the abstraction functionabs
are both definitionally injective — which, if they are record fields, they will be; but a concrete implementation ofabs
, like below, will generally fail this.This could be solved by making
abs
into anopaque
function, but then Agda would be unable to compute howabs
andinst
interact, making it hard to apply the computation rule for an abstraction.
Therefore, we prefer to define pcas to have an abstraction elimination function. But the and combinators are still important, for example when equipping a type with a pca structure. This module is dedicated to proving that if we have the combinators and then we can get out a pca structure. First, we write out the actual battery of conditions lined out in our first complaint:
record has-ski (_%_ : ↯ A → ↯ A → ↯ A) : Type (level-of A) where
field
: ↯ A
K S
: ⌞ K ⌟
K↓ : ⌞ S ⌟
S↓
: ∀ {x} → ⌞ x ⌟ → ⌞ K % x ⌟
K↓₁ : ∀ {x y} → ⌞ x ⌟ → ⌞ y ⌟ → (K % x) % y ≡ x
K-β
: ∀ {f} → ⌞ f ⌟ → ⌞ S % f ⌟
S↓₁ : ∀ {f g} → ⌞ f ⌟ → ⌞ g ⌟ → ⌞ (S % f) % g ⌟
S↓₂ : ∀ {f g x} → ⌞ f ⌟ → ⌞ g ⌟ → ⌞ x ⌟ → ((S % f) % g) % x ≡ ((f % x) % (g % x)) S-β
Note that this isn’t the only set of combinators that results in a pca, and keep in mind that the combinators themselves need not be unique. We say that a set of combinators which can be used to build an abstraction procedure is combinatorially complete.
module _ {A : Type ℓ} {_%_ : ↯ A → ↯ A → ↯ A} (pca : has-ski _%_) (let infixl 8 _%_; _%_ = _%_) where
open has-ski pca
open eval _%_
We start by naming some useful terms. First, since and are both defined, we can make them into terms; Furthermore, we can show that is defined, and we make it into a term we refer to as
private
: ↯ A
i = (S % K) % K
i
: ∀ {n} → Term A n
`K `S `I = const (K , K↓)
`K = const (S , S↓)
`S = const (i , S↓₂ K↓ K↓) `I
Now suppose that we have a term in variables, say and we want to abstract over making it into a term of variables which is a “function of ”. We do this by cases. First, abstracting over in itself gives the identity function, while other variables are shifted down by one position and guarded by the combinator.
: Term A (suc n) → Term A n
abs (var n) with fin-view n
abs ... | zero = `I
... | suc i = app `K (var i)
These satisfy and as required. Arbitrary constants are also guarded by the combinator, since they are independent of the new argument. Finally, for a complex term like we first abstract over in the subterms and and combine the results using
(const t) = app `K (const t)
abs (app f x) = app (app `S (abs f)) (abs x) abs
This last case shows that the combinator serves to “share” the new variable among the subterms. We’ve already argued that the result of abstracting over in a variable is defined; these latter two cases are similarly easy.
: (t : Term A (suc n)) (ρ : Vec (↯⁺ A) n) → ⌞ eval (abs t) ρ ⌟
abs↓ (var n) ρ with fin-view n
abs↓ ... | zero = S↓₂ K↓ K↓
... | suc i = K↓₁ (lookup ρ i .snd)
(const x) ρ = K↓₁ (x .snd)
abs↓ (app f x) ρ = S↓₂ (abs↓ f ρ) (abs↓ x ρ) abs↓
Finally, we must prove that abs
commutes with inst
; this is
entirely calculational, and we write out all the cases for clarity.
abs-β: (t : Term A (suc n)) (ρ : Vec (↯⁺ A) n) (a : ↯⁺ A)
→ eval (abs t) ρ % a .fst ≡ eval (inst t (const a)) ρ
(var x) ρ a with fin-view x
abs-β ... | zero =
.fst ≡⟨ S-β K↓ K↓ (a .snd) ⟩
S % K % K % a .fst % (K % a .fst) ≡⟨ K-β (a .snd) (K↓₁ (a .snd)) ⟩
K % a .fst ∎
a ... | suc i =
(var i) ρ % a .fst ≡⟨ K-β (lookup ρ i .snd) (a .snd) ⟩
K % eval (var i) ρ ∎
eval
(const x) ρ a =
abs-β (const x) ρ % a .fst ≡⟨ K-β (x .snd) (a .snd) ⟩
K % eval (const x) ρ ∎
eval
(app f x) ρ a =
abs-β (abs f) ρ % eval (abs x) ρ % a .fst ≡⟨ S-β (abs↓ f ρ) (abs↓ x ρ) (a .snd) ⟩
S % eval (eval (abs f) ρ % a .fst % (eval (abs x) ρ % a .fst)) ≡⟨ ap₂ _%_ (abs-β f ρ a) (abs-β x ρ a) ⟩
(eval (inst f (const a)) ρ % eval (inst x (const a)) ρ) ∎
We’re thus free to provide a combinatorial basis, instead of an abstraction procedure, when constructing a PCA.
: is-pca _%_
has-ski→is-pca {-# INLINE has-ski→is-pca #-}
= record { abs = abs ; abs↓ = abs↓ ; abs-β = abs-β } has-ski→is-pca
References
- Bauer, Andrej. 2022. “Notes on Realizability.” Midlands Graduate School. https://github.com/andrejbauer/notes-on-realizability.
- de Jong, Tom. 2024. “Categorical Realizability.” Midlands Graduate School. https://github.com/tomdjong/MGS-categorical-realizability.