Brief background: I'm implementing contexts and renamings using de Bruijn indices, and then extending those notions with an "undefined" name, written ε. The undefined name induces a partial order on names in Γ, and also on renamings between Γ and Γ'. Looking up a name in a renaming (get) and a related function put which inverts get in a specific way give rise to a (rather trivial) Galois connection. Here's the pared-down code:
module Temp where
open import Relation.Binary
open import Data.Fin using (Fin; zero; suc)
open import Data.Maybe renaming (nothing to ε; just to [_]) hiding (map)
open import Data.Nat using (ℕ; zero; suc)
open import Data.Product
open import Function
open import Relation.Binary.PropositionalEquality
-- Pointed version of Fin, with ε indicating an undefined name.
Name : ℕ → Set
Name = Maybe ∘ Fin
-- The type of names below x.
data ↓_ {Γ} : Name Γ → Set where
ε : {x : Name Γ} → ↓ x
[_] : (x : Fin Γ) → ↓ [ x ]
-- A proof that two upper-bounded names are related.
data _≤_ {Γ} : {x : Name Γ} → ↓ x → ↓ x → Set where
ε : {x : Name Γ} {x : ↓ x} → ε ≤ x
[_] : (x : Fin Γ) → [ x ] ≤ [ x ]
≤-refl : ∀ {Γ} {y : Name Γ} → Reflexive (_≤_ {x = y})
≤-refl {x = ε} = ε
≤-refl {x = [ x ]} = [ x ]
-- Functorial action of suc on a name.
suc⁺₀ : ∀ {Γ} → Name Γ → Name (suc Γ)
suc⁺₀ ε = ε
suc⁺₀ [ x ] = [ suc x ]
-- Lifting of suc⁺₀ to down-sets.
suc⁺ : ∀ {Γ} {x : Name Γ} → ↓ x → ↓ suc⁺₀ x
suc⁺ ε = ε
suc⁺ [ x ] = [ suc x ]
-- A renaming from Γ Γ′.
data Ren : ℕ → ℕ → Set where
[] : ∀ {Γ} → Ren zero Γ
_∷_ : ∀ {Γ Γ′} → Name Γ′ → Ren Γ Γ′ → Ren (suc Γ) Γ′
-- The type of renamings below ρ.
data Ren-↓_ : ∀ {Γ Γ′} → Ren Γ Γ′ → Set where
[] : ∀ {Γ} → Ren-↓ ([] {Γ})
_∷_ : ∀ {Γ Γ′} {x : Name Γ′} {ρ : Ren Γ Γ′} →
↓ x → Ren-↓ ρ → Ren-↓ (x ∷ ρ)
-- Least renaming below ρ.
Ren-ε : ∀ {Γ Γ′} {ρ : Ren Γ Γ′} → Ren-↓ ρ
Ren-ε {ρ = []} = []
Ren-ε {ρ = _ ∷ _} = ε ∷ Ren-ε
-- Interpret a renaming as a function.
get₀ : ∀ {Γ Γ′} → Ren Γ Γ′ × Name Γ → Name Γ′
get₀ (_ , ε) = ε
get₀ (x ∷ _ , [ zero ]) = x
get₀ (_ ∷ ρ , [ suc y ]) = get₀ (ρ , [ y ])
-- Lift get₀ to down-sets.
get : ∀ {Γ Γ′} {ρ : Ren Γ Γ′} {x : Name Γ} →
Ren-↓ ρ × ↓ x → ↓ get₀ (ρ , x)
get (_ , ε) = ε
get (x ∷ _ , [ zero ]) = x
get ( _ ∷ ρ , [ suc x ]) = get (ρ , [ x ])
-- Lower adjoint of get.
put : ∀ {Γ Γ′} {ρ : Ren Γ Γ′} (x : Name Γ) →
↓ get₀ (ρ , x) → Ren-↓ ρ × ↓ x
put ε ε = Ren-ε , ε
put {ρ = _ ∷ _} [ zero ] ε = Ren-ε , ε
put {ρ = ._ ∷ _} [ zero ] [ x ] = [ x ] ∷ Ren-ε , [ zero ]
put {ρ = _ ∷ _} [ suc x ] u = map (_∷_ ε) suc⁺ (put [ x ] u)
That's the set up. What I'd now like to do is show that get and put form a Galois connection. One way to do this is to show that each function is monotone, and that of their two composites, one is inflationary, and the other deflationary. This is all fine, except that I get stuck trying to show that the get∘put composite is inflationary:
id≤get∘put : ∀ {Γ Γ′} {ρ : Ren Γ Γ′} (x : Name Γ)
(u : ↓ get₀ (ρ , x)) → u ≤ get (put x u)
id≤get∘put ε ε = ε
id≤get∘put {ρ = _ ∷ _} [ zero ] ε = ε
id≤get∘put {ρ = ._ ∷ _} [ zero ] [ _ ] = ≤-refl
id≤get∘put {ρ = y ∷ ρ} [ suc x ] u
with put [ x ] u | id≤get∘put {ρ = ρ} [ x ] u
... | ρ′ , x′ | u′ = {!!}
In my context, I have that u ≤ get (ρ′ , x′) from the recursive invocation of the function. I would like to be able to use this to derive the goal, namely u ≤ get (ε ∷ ρ′ , suc⁺ x′). Intuitively, this should be easy, as looking up x' in ρ′ should be the same as looking up the successor of x' in ρ′ extended with an additional name. In fact it should be easy to show that get (ρ′ , x′) equals get (ε ∷ ρ′ , suc⁺ x′).
But if I try to state the corresponding lemma:
-- Not typeable, because ≡ requires homogeneous types?
postulate
not-ok : ∀ {Γ Γ′} {ρ : Ren Γ Γ′} {x : Name Γ}
(ρ′ : Ren-↓ ρ) (x′ : ↓ x) → get (ρ′ , x′) ≡ get (ε ∷ ρ′ , suc⁺ x′)
then Agda complains because the two invocations of get (which is get₀ lifted to down-sets) have different types. This is because the type of get mentions the value computed by get₀.
However, the two invocations don't "really" have different types, in that I can prove the following lemma:
-- This is related to the lemma I want, but not quite it.
ok : ∀ {Γ Γ′} (ρ₀ : Ren Γ Γ′) (x₀ : Name Γ) →
get₀ (ρ₀ , x₀) ≡ get₀ (ε ∷ ρ₀ , suc⁺₀ x₀)
ok _ ε = refl
ok (_ ∷ _) [ zero ] = refl
ok (_ ∷ ρ) [ suc x ] = ok ρ [ x ]
I haven't yet used heterogeneous equality in Agda, and to be honest I don't really know how to use it. Would it allow me to state the lemma I need?
In fact it should be easy to show that get (ρ′ , x′) equals get (ε ∷ ρ′ , suc⁺ x′).
First, the reason that Agda does not see this equality is that the functions suc⁺ doesn't reduce for an argument of a general form x'. This is because your definition of suc⁺ pattern matches on the argument to see if it is a ε or a [_]. So, the simplest way to go further is to give Agda more information about the argument by pattern matching on x', so that suc⁺ x′ can reduce:
id≤get∘put {ρ = y ∷ ρ} [ suc x ] u
with put [ x ] u | id≤get∘put {ρ = ρ} [ x ] u
... | ρ′ , ε | u′ = u′
... | ρ′ , [ ._ ] | u′ = u′
With this extra pattern match, the type of u' reduces further to the expected type and everything works.
In your lemma not-ok, you have a very similar problem, that can be solved most easily by stating the lemma separately for x = ε or x = [_].
Working with heterogeneous equality is an option you might also explore to fix your problem, but I expect it will be more difficult than the options proposed here.
Related
I'm trying to prove a weakening lemma analagous to Harper's from chapter 4 of PFPL. Namely, weakening : {x : String} {Γ : Context} {e : Expr} {τ τ' : Type} → x ∉dom Γ → Γ ⊢ e ؛ τ' → (Γ , x ؛ τ) ⊢ e ؛ τ'
I've adapted some of Wadler's code, where he proves weaken below, but still don't know how to prove this general weakening lemma, either using the rename function or by induction as harper does. (for example, Harper seems to implicitly assume exchange for a let constructor, not included in this language). I thought introducing the _∉dom_ would help, but I just see it bloating the amoung of work I have to do in somehow making a bunch of correspondance lemmas with _؛_∈_.
How does one prove weakening, either as stated or modified, with either via induction or rename?
module basic where
open import Data.List using (List; _∷_; []; map)
open import Data.Empty
open import Data.String using (_++_; _==_; _≟_; String)
open import Data.Nat using (ℕ)
import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl; trans; sym; cong; cong-app; subst)
data Type : Set where
nat : Type
bool : Type
data Expr : Set where
var : String → Expr
lit : (n : ℕ) → Expr
tt : Expr
ff : Expr
_+'_ : Expr → Expr → Expr
_*'_ : Expr → Expr → Expr
_<'_ : Expr → Expr → Expr
if : Expr → Expr → Expr → Expr
Id : Set
Id = String
infixl 5 _,_؛_
data Context : Set where
∅ : Context
_,_؛_ : Context → Id → Type → Context
data _؛_∈_ : Id → Type → Context → Set where
Z : ∀ {Γ x A} → x ؛ A ∈ (Γ , x ؛ A)
S : ∀ {Γ x y A B} → (x ≡ y → ⊥) → x ؛ A ∈ Γ → x ؛ A ∈ (Γ , y ؛ B)
-- not in wadler
data _∉dom_ : Id → Context → Set where
em : ∀ {x} → x ∉dom ∅
notcons : ∀ {x y τ Γ} → x ∉dom Γ → (x ≡ y → ⊥) → x ∉dom (Γ , y ؛ τ )
-- hypothetical judgement
data _⊢_؛_ : Context → Expr → Type → Set where
varR : ∀ {a τ Γ} → (a ؛ τ ∈ Γ) → (Γ ⊢ (var a) ؛ τ)
natR : ∀ {Γ} {n : ℕ} → Γ ⊢ (lit n) ؛ nat
trueR : ∀ {Γ} → Γ ⊢ tt ؛ bool
falseR : ∀ {Γ} → Γ ⊢ ff ؛ bool
plus-i : ∀ {Γ} {e1 e2 : Expr} → Γ ⊢ e1 ؛ nat → Γ ⊢ e2 ؛ nat → Γ ⊢ e1 +' e2 ؛ nat
times-i : ∀ {Γ} {e1 e2 : Expr} → Γ ⊢ e1 ؛ nat → Γ ⊢ e2 ؛ nat → Γ ⊢ e1 *' e2 ؛ nat
le-i : ∀ {Γ} {e1 e2 : Expr} → Γ ⊢ e1 ؛ nat → Γ ⊢ e2 ؛ nat → Γ ⊢ e1 <' e2 ؛ bool
if-i : ∀ {Γ} {τ} {e1 e2 e3 : Expr} → Γ ⊢ e1 ؛ bool → Γ ⊢ e2 ؛ τ → Γ ⊢ e3 ؛ τ → Γ ⊢ if e1 e2 e3 ؛ τ
-- adapted from wadler
rename : ∀ {Γ Δ} → (∀ {x A} → x ؛ A ∈ Γ → x ؛ A ∈ Δ) → (∀ {M A} → Γ ⊢ M ؛ A → Δ ⊢ M ؛ A)
rename f (varR x) = varR (f x)
rename f natR = natR
rename f trueR = trueR
rename f falseR = falseR
rename f (plus-i h h₁) = plus-i (rename f h) (rename f h₁)
rename f (times-i h h₁) = times-i (rename f h) (rename f h₁)
rename f (le-i h h₁) = le-i (rename f h) (rename f h₁)
rename f (if-i h h₁ h₂) = if-i (rename f h) (rename f h₁) (rename f h₂)
-- wadler's weaken lemma
weaken : ∀ {Γ M A} → ∅ ⊢ M ؛ A → Γ ⊢ M ؛ A
weaken x = rename (λ ()) x
-- my attempt
weakening : {x : String} {Γ : Context} {e : Expr} {τ τ' : Type} → x ∉dom Γ → Γ ⊢ e ؛ τ' → (Γ , x ؛ τ) ⊢ e ؛ τ'
-- induction, dunno how to account for the variable
weakening x (varR y) = {!!}
weakening x natR = natR
weakening x trueR = trueR
weakening x falseR = falseR
weakening x (plus-i y₁ y₂) = plus-i (weakening x y₁) (weakening x y₂)
weakening x (times-i y₁ y₂) = times-i (weakening x y₁) (weakening x y₂)
weakening x (le-i y₁ y₂) = le-i (weakening x y₁) (weakening x y₂)
weakening x (if-i y₁ y₂ y₃) = if-i (weakening x y₁) (weakening x y₂) (weakening x y₃)
-- otherwise, i don't know how to addapt this rename
weakening' : {x : String} {Γ : Context} {e : Expr} {τ τ' : Type} → x ∉dom Γ → Γ ⊢ e ؛ τ' → (Γ , x ؛ τ) ⊢ e ؛ τ'
weakening' {x} {τ = τ} em y = rename help y
where
help : {x = x₁ : Id} {A : Type} → x₁ ؛ A ∈ ∅ → x₁ ؛ A ∈ (∅ , x ؛ τ)
help {x = x₁} ()
weakening' (notcons α β) y = rename (λ z → S (λ x₃ → {!!}) z) y
A possible idea is to add a lemma which states that, if a value x is not in a context Γ and if a value a is of type τ in the same context Γ then a and x cannot be equal.
open import Relation.Nullary
-- If a,τ'∈Γ and x∉Γ then ¬a≡x
prop : ∀ {x Γ τ a} → x ∉dom Γ → a ؛ τ ∈ Γ → ¬ a ≡ x
prop (notcons _ ¬x≡x) Z refl = ¬x≡x refl
prop (notcons x∉Γ _) (S _ aτ∈Γ) = prop x∉Γ aτ∈Γ
Then the missing case in your version of weakening simple becomes
weakening x (varR y) = varR (S (prop x y) y)
Suppose I have a transitive relation ~with two endomaps f and g.
Assuming f and g agree everywhere and f a ~ f b ~ f c
then there are two ways to show g a ~ g c:
transform each f into a g by the given equality then apply
transitivity,
or apply transitivity then transform along the equality.
Are the resulting proofs identical? Apparently so,
open import Relation.Binary.PropositionalEquality
postulate A : Set
postulate _~_ : A → A → Set
postulate _⟨~~⟩_ : ∀{a b c} → a ~ b → b ~ c → a ~ c
postulate f g : A → A
subst-dist : ∀{a b c}{ef : f a ~ f b}{psf : f b ~ f c} → (eq : ∀ {z} → f z ≡ g z)
→
subst₂ _~_ eq eq ef ⟨~~⟩ subst₂ _~_ eq eq psf
≡ subst₂ _~_ eq eq (ef ⟨~~⟩ psf)
subst-dist {a} {b} {c} {ef} {psf} eq rewrite eq {a} | eq {b} | eq {c} = refl
I just recently learned about the rewrite keyword and thought it might help here; apparently it does. However, I honestly do not understand what is going on here. I've used rewrite other times, with comprehension. However, all these substs are confusing me.
I'd like to know
if is there a simplier way to obtain subst-dist? Maybe something similar in the libraries?
what is going on with this particular usage of rewrite
an alternate proof of subst-dist without using rewrite (most important)
is there another way to obtain g a ~ g c without using subst?
what are some of the downsides of using heterogeneous equality, it doesn't seem like most people are fond of it. (also important)
Any help is appreciated.
rewrite is just a sugared with, which is just sugared "top-level" pattern matching. See in Agda’s documentation.
what are some of the downsides of using heterogeneous equality, it
doesn't seem like most people are fond of it. (also important)
This is OK
types-equal : ∀ {α} {A B : Set α} {x : A} {y : B} -> x ≅ y -> A ≡ B
types-equal refl = refl
this is OK as well
A-is-Bool : {A : Set} {x : A} -> x ≅ true -> A ≡ Bool
A-is-Bool refl = refl
This is an error
fail : ∀ {n m} {i : Fin n} {j : Fin m} -> i ≅ j -> n ≡ m
fail refl = {!!}
-- n != m of type ℕ
-- when checking that the pattern refl has type i ≅ j
because Fin n ≡ Fin m doesn't immediately imply n ≡ m (you can make it so by enabling --injective-type-constructors, but that makes Agda anti-classical) (Fin n ≡ Fin m -> n ≡ m is provable though).
Originally Agda permitted to pattern match on x ≅ y when x and y have non-unifiable types, but that allows to write weird things like (quoting from this thread)
P : Set -> Set
P S = Σ S (\s → s ≅ true)
pbool : P Bool
pbool = true , refl
¬pfin : ¬ P (Fin 2)
¬pfin ( zero , () )
¬pfin ( suc zero , () )
¬pfin ( suc (suc ()) , () )
tada : ¬ (Bool ≡ Fin 2)
tada eq = ⊥-elim ( ¬pfin (subst (\ S → P S) eq pbool ) )
Saizan or maybe it's just ignoring the types and comparing the constructor names?
pigworker Saizan: that's exactly what I think is happening
Andread Abel:
If I slighly modify the code, I can prove Bool unequal Bool2, where true2, false2 : Bool2 (see file ..22.agda)
However, if I rename the constructors to true, false : Bool2, then suddenly I cannot prove that Bool is unequal to Bool2 anymore (see
other file). So, at the moment Agda2 compares apples and oranges in
certain situations. ;-)
So in order to pattern match on i ≅ j, where i : Fin n, j : Fin m, you first need to unify n with m
OK : ∀ {n m} {i : Fin n} {j : Fin m} -> n ≡ m -> i ≅ j -> ...
OK refl refl = ...
That's the main drawback of heteregeneous equality: you need to provide proofs of equality of indices everywhere. Usual cong and subst are non-indexed, so you also have to provide indexed versions of them (or use even more annoying cong₂ and subst₂).
There is no such problem with "heteroindexed" (I don't know if it has a proper name) equality
data [_]_≅_ {ι α} {I : Set ι} {i} (A : I -> Set α) (x : A i) : ∀ {j} -> A j -> Set where
refl : [ A ] x ≅ x
e.g.
OK : ∀ {n m} {i : Fin n} {j : Fin m} -> [ Fin ] i ≅ j -> n ≡ m
OK refl = refl
More generally, whenever you have x : A i, y : A j, p : [ A ] x ≅ y, you can pattern match on p and j will be unified with i, so you don't need to carry an additional proof of n ≡ m.
Heterogeneous equality, as it presented in Agda, is also inconsistent with the univalence axiom.
EDIT
Pattern matching on x : A, y : B, x ≅ y is equal to pattern matching on A ≡ B and then changing every y in a context to x. So when you write
fail : ∀ {n m} {i : Fin n} {j : Fin m} -> i ≅ j -> n ≡ m
fail refl = {!!}
it's the same as
fail' : ∀ {n m} {i : Fin n} {j : Fin m} -> Fin n ≡ Fin m -> i ≅ j -> n ≡ m
fail' refl refl = {!!}
but you can't pattern match on Fin n ≡ Fin m
fail-coerce : ∀ {n m} -> Fin n ≡ Fin m -> Fin n -> Fin m
fail-coerce refl = {!!}
-- n != m of type ℕ
-- when checking that the pattern refl has type Fin n ≡ Fin m
like you cannot pattern match on
fail'' : ∀ {n m} -> Nat.pred n ≡ Nat.pred m -> n ≡ m
fail'' refl = {!!}
-- n != m of type ℕ
-- when checking that the pattern refl has type Nat.pred n ≡ Nat.pred m
In general
f-inj : ∀ {n m} -> f n ≡ f m -> ...
f-inj refl = ...
works only if f is obviously injective. I.e. if f is a series of constructors (e.g. suc (suc n) ≡ suc (suc m)) or computes to it (e.g. 2 + n ≡ 2 + m). Type constructors (which Fin is) are not injective because that would make Agda anti-classical, so you cannot pattern on Fin n ≡ Fin m unless you enable --injective-type-constructors.
Indices unify for
data [_]_≅_ {ι α} {I : Set ι} {i} (A : I -> Set α) (x : A i) : ∀ {j} -> A j -> Set where
refl : [ A ] x ≅ x
because you don't try to unify A i with A j, but instead explicitly carry indices in the type of [_]_≅_, which make them available for unification. When indices are unified, both types become the same A i and it's possible to proceed like with propositional equality.
EDIT
One another problem with heterogeneous equality is that it's not fully heterogeneous: in x : A, y : B, x ≅ y A and B must be in the same universe. The treatment of universe levels in data definitions has been changed recently and now we can define fully heterogeneous equality:
data _≅_ {α} {A : Set α} (x : A) : ∀ {β} {B : Set β} -> B -> Set where
refl : x ≅ x
But this doesn't work
levels-equal : ∀ {α β} -> Set α ≅ Set β -> α ≅ β
levels-equal refl = refl
-- Refuse to solve heterogeneous constraint Set α : Set (suc α) =?=
-- Set β : Set (suc β)
because Agda doesn't think suc is injective
suc-inj : {α β : Level} -> suc α ≅ suc β -> α ≅ β
suc-inj refl = refl
-- α != β of type Level
-- when checking that the pattern refl has type suc α ≅ suc β
If we postulate it, then we can prove levels-equal:
hcong : ∀ {α β δ} {A : Set α} {B : Set β} {D : Set δ} {x : A} {y : B}
-> (f : ∀ {γ} {C : Set γ} -> C -> D) -> x ≅ y -> f x ≅ f y
hcong f refl = refl
levelOf : ∀ {α} {A : Set α} -> A -> Level
levelOf {α} _ = α
postulate
suc-inj : {α β : Level} -> suc α ≅ suc β -> α ≅ β
levels-equal : ∀ {α β} -> Set α ≅ Set β -> α ≅ β
levels-equal p = suc-inj (suc-inj (hcong levelOf p))
It seems that to prove that two items of a record type are equivalent, I need to write a helper that takes component wise proofs and applies them.
An example:
postulate P : ℕ → Set
record Silly : Set (ℓsuc ℓ₀) where
constructor _#_#_
field
n : ℕ
pn : P n
f : Set → ℕ
open Silly
SillyEq : ∀ s t → n s ≡ n t → pn s ≅ pn t → f s ≡ f t → s ≡ t
SillyEq (n # pn # f) (.n # .pn # .f) ≡-refl ≅-refl ≡-refl = ≡-refl
I feel like SillyEq should somehow be available to me, that I do not need to write it myself --or am I mistaken.
Also, I could not prove SillyEq without declaring a constructor and then pattern matching on it.
Thanks for your assistance!
Having
SillyEq' : ∀ {n₁ n₂ pn₁ pn₂ f₁ f₂}
→ n₁ ≡ n₂ → pn₁ ≅ pn₂ → f₁ ≡ f₂ → (n₁ # pn₁ # f₁) ≡ (n₂ # pn₂ # f₂)
you can prove SillyEq as
SillyEq : ∀ s t → n s ≡ n t → pn s ≅ pn t → f s ≡ f t → s ≡ t
SillyEq _ _ = SillyEq'
due to the η-rule for Silly. So if you have an arity-generic version of cong, then you can prove SillyEq as (note the heterogeneous equality everywhere)
SillyEq : ∀ s t → n s ≅ n t → pn s ≅ pn t → f s ≅ f t → s ≅ t
SillyEq _ _ = gcong 3 _#_#_
I don't know whether gcong can be easily expressed via reflection, but I guess it can be written using the usual arity-generic programming stuff (like here), but the solution won't be short.
Here is an ad hoc proof:
cong₃ : ∀ {α β γ δ} {A : Set α} {B : A -> Set β} {C : ∀ {x} -> B x -> Set γ}
{D : ∀ {x} {y : B x} -> C y -> Set δ} {x y v w s t}
-> (f : ∀ x -> (y : B x) -> (z : C y) -> D z)
-> x ≅ y -> v ≅ w -> s ≅ t -> f x v s ≅ f y w t
cong₃ f refl refl refl = refl
SillyEq : ∀ s t → n s ≅ n t → pn s ≅ pn t → f s ≅ f t → s ≅ t
SillyEq _ _ = cong₃ _#_#_
However, a mix of propositional and heterogeneous equalities like in your case complicates everything.
We can enumerate the elements of a list like this:
-- enumerate-ℕ = zip [0..]
enumerate-ℕ : ∀ {α} {A : Set α} -> List A -> List (ℕ × A)
enumerate-ℕ = go 0 where
go : ∀ {α} {A : Set α} -> ℕ -> List A -> List (ℕ × A)
go n [] = []
go n (x ∷ xs) = (n , x) ∷ go (ℕ.suc n) xs
E.g. enumerate-ℕ (1 ∷ 3 ∷ 2 ∷ 5 ∷ []) is equal to (0 , 1) ∷ (1 , 3) ∷ (2 , 2) ∷ (3 , 5) ∷ []. Assuming there is sharing in Agda, the function is linear.
However if we try to enumerate the elements of a list by Fins rather than ℕs, the function becomes quadratic:
enumerate-Fin : ∀ {α} {A : Set α} -> (xs : List A) -> List (Fin (length xs) × A)
enumerate-Fin [] = []
enumerate-Fin (x ∷ xs) = (zero , x) ∷ map (pmap suc id) (enumerate-Fin xs)
Is it possible to enumerate by Fins in linear time?
Consider this as a first attempt:
go : ∀ {m α} {A : Set α} -> Fin m -> (xs : List A) -> List (Fin (m + length xs) × A)
go i [] = []
go i (x ∷ xs) = (inject+ _ i , x) ∷ {!go (suc i) xs!}
i grows at each recursive call as it should, but there is a mismatch:
the type of the goal is List (Fin (.m + suc (length xs)) × .A)
the type of the expression insise the hole is List (Fin (suc (.m + length xs)) × .A)
It's easy to prove that two types are equal, but it's also dirty. This is a common problem: one argument grows and the other lowers, so we need definitionally commutative _+_ to handle both the cases, but there is no way to define it. The solution is to use CPS:
go : ∀ {α} {A : Set α} -> (k : ℕ -> ℕ) -> (xs : List A) -> List (Fin (k (length xs)) × A)
go k [] = []
go k (x ∷ xs) = ({!!} , x) ∷ go (k ∘ suc) xs
(k ∘ suc) (length xs) is the same thing as k (length (x ∷ xs)), hence the mismatch is fixed, but what is i now? The type of the hole is Fin (k (suc (length xs))) and it's uninhabited in the current context, so let's introduce some inhabitant:
go : ∀ {α} {A : Set α}
-> (k : ℕ -> ℕ)
-> (∀ {n} -> Fin (k (suc n)))
-> (xs : List A)
-> List (Fin (k (length xs)) × A)
go k i [] = []
go k i (x ∷ xs) = (i , x) ∷ go (k ∘ suc) {!!} xs
The type of the new hole is {n : ℕ} → Fin (k (suc (suc n))). We can fill it with i, but i must grow at each recursive call. However suc and k doesn't commute, so suc i is Fin (suc (k (suc (_n_65 k i x xs)))). So we add another argument that sucs under k, and the final definition is
enumerate-Fin : ∀ {α} {A : Set α} -> (xs : List A) -> List (Fin (length xs) × A)
enumerate-Fin = go id suc zero where
go : ∀ {α} {A : Set α}
-> (k : ℕ -> ℕ)
-> (∀ {n} -> Fin (k n) -> Fin (k (suc n)))
-> (∀ {n} -> Fin (k (suc n)))
-> (xs : List A)
-> List (Fin (k (length xs)) × A)
go k s i [] = []
go k s i (x ∷ xs) = (i , x) ∷ go (k ∘ suc) s (s i) xs
which works, because s : {n : ℕ} → Fin (k n) → Fin (k (suc n)) can be treated as {n : ℕ} → Fin (k (suc n)) → Fin (k (suc (suc n))).
A test: C-c C-n enumerate-Fin (1 ∷ 3 ∷ 2 ∷ 5 ∷ []) gives
(zero , 1) ∷
(suc zero , 3) ∷
(suc (suc zero) , 2) ∷ (suc (suc (suc zero)) , 5) ∷ []
Now note that in enumerate-Fin k always follows Fin in the types. Hence we can abstract Fin ∘ k and get a generic version of the function that works with both ℕ and Fin:
genumerate : ∀ {α β} {A : Set α}
-> (B : ℕ -> Set β)
-> (∀ {n} -> B n -> B (suc n))
-> (∀ {n} -> B (suc n))
-> (xs : List A)
-> List (B (length xs) × A)
genumerate B s i [] = []
genumerate B s i (x ∷ xs) = (i , x) ∷ genumerate (B ∘ suc) s (s i) xs
enumerate-ℕ : ∀ {α} {A : Set α} -> List A -> List (ℕ × A)
enumerate-ℕ = genumerate _ suc 0
enumerate-Fin : ∀ {α} {A : Set α} -> (xs : List A) -> List (Fin (length xs) × A)
enumerate-Fin = genumerate Fin suc zero
With the following definition of equality, we have refl as constructor
data _≡_ {a} {A : Set a} (x : A) : A → Set a where
refl : x ≡ x
and we can prove that function are congruent on equality
cong : ∀ { a b} { A : Set a } { B : Set b }
(f : A → B ) {m n} → m ≡ n → f m ≡ f n
cong f refl = refl
I am not sure I can parse what is going on exactly here.
I think we are pattern matching refl on hidden parameters : if we replace the first occurence by refl by another identifier, we get a type error.
after pattern matching, I imagine that m and n are the same by the definition of refl. then magic occurs (a definition of functionality of a relation is applied ? or is it build in ?)
Is there an intuitive description on what is going on ?
Yes, the arguments in curly braces {} are implicit and they only need to be supplied or matched if agda cannot figure them out. It is necessary to specify them, since dependent types needs to refer to the values they depend on, but dragging them around all the time would make the code rather clunky.
The expression cong f refl = refl matches the explicit arguments (A → B) and (m ≡ n). If you wanted to match the implicit arguments, you'd need to put the matching expression in {}, but here there is no need for that. Then on the right hand side it is indeed the construction of (f m ≡ f n) using refl, and it works "by magic". Agda has a built-in axiom that proves this to be true. That axiom is similar (but stronger than) J-axiom - the induction axiom: if something C : (x y : A) → (x ≡ y) → Set is true for C x x refl, then it is also true for any x y : A and p : x ≡ y.
J : forall {A : Set} {C : (x y : A) → (x ≡ y) → Set} →
(c : ∀ x → C x x refl) →
(x y : A) → (p : x ≡ y) → C x y p
-- this really is an axiom, but in Agda there is a stronger built-in,
-- which can be used to prove this
J c x .x refl = c x -- this _looks_ to only mean x ≡ x
-- but Agda's built-in extends this proof to all cases
-- for which x ≡ y can be constructed - that's the point
-- of having induction
cong : ∀ { a b} { A : Set a } { B : Set b }
(f : A → B ) {m n} → m ≡ n → f m ≡ f n
cong f {x} {y} p = J {C = \x y p → f x ≡ f y} -- the type of equality
-- of function results
(\_ → refl) -- f x ≡ f x is true indeed
x y p
(In this last line we: match explicit arguments f and p, and also the implicit arguments m=x and n=y. Then we pass to J one implicit argument, but it is not the first positional implicit, so we tell agda that it is C in the definition - without doing that, Agda won't see what type is meant by refl in \_ → refl)