------------------------------------------------------------------------ -- The list container ------------------------------------------------------------------------ {-# OPTIONS --without-K #-} module Container.List where open import Bag-equivalence using () renaming (_≈-bag_ to _≈-bagL_; _∈_ to _∈L_; Any to AnyL) open import Container open import Equality.Propositional open import Equivalence using (_⇔_; module _⇔_) open import Fin open import Prelude as P hiding (List; []; _∷_; foldr; _++_; id; _∘_) import Bijection open Bijection equality-with-J using (_↔_; module _↔_; Σ-≡,≡↔≡) import Function-universe open Function-universe equality-with-J import H-level.Closure open H-level.Closure equality-with-J ------------------------------------------------------------------------ -- The type -- Lists. List : Container lzero List = ℕ ▷ Fin ------------------------------------------------------------------------ -- The definitions of lists and bag equivalence for lists given in -- Container/Container.List and in Prelude/Bag-equivalence are closely -- related -- The two definitions of lists are equivalent. List⇔List : {A : Set} → ⟦ List ⟧ A ⇔ P.List A List⇔List {A} = record { to = to ; from = λ xs → (length xs , P.lookup xs) } where to : ⟦ List ⟧ A → P.List A to (zero , f) = P.[] to (suc n , f) = P._∷_ (f (inj₁ tt)) (to (n , f ∘ inj₂)) -- If we assume that equality of functions is extensional, then we can -- also prove that the two definitions are isomorphic. List↔List : {A : Set} → ({B : Set} → Extensionality B (λ _ → A)) → ⟦ List ⟧ A ↔ P.List A List↔List {A} ext = record { surjection = record { equivalence = List⇔List ; right-inverse-of = to∘from } ; left-inverse-of = from∘to } where open _⇔_ List⇔List to∘from : ∀ xs → to (from xs) ≡ xs to∘from P.[] = refl to∘from (P._∷_ x xs) = cong (P._∷_ x) (to∘from xs) from∘to : ∀ xs → from (to xs) ≡ xs from∘to (zero , f) = cong (_,_ _) (ext λ ()) from∘to (suc n , f) = (suc (length (to xs)) , P.lookup (P._∷_ x (to xs))) ≡⟨ lemma₃ (from∘to (n , f ∘ inj₂)) ⟩ (suc n , [ (λ _ → x) , f ∘ inj₂ ]) ≡⟨ lemma₁ ⟩∎ (suc n , f) ∎ where x = f (inj₁ tt) xs = (n , f ∘ inj₂) lemma₁ : ∀ {n f} → _≡_ {A = ⟦ List ⟧ A} (suc n , [ (λ _ → f (inj₁ tt)) , f ∘ inj₂ ]) (suc n , f) lemma₁ = cong (_,_ _) (ext [ (λ { tt → refl }) , (λ _ → refl) ]) lemma₂ : {n : ℕ} {lkup : Fin n → A} → (≡n : length (to xs) ≡ n) → subst (λ n → Fin n → A) ≡n (P.lookup (to xs)) ≡ lkup → _≡_ {A = ⟦ List ⟧ A} (suc (length (to xs)) , P.lookup (P._∷_ x (to xs))) (suc n , [ (λ _ → x) , lkup ]) lemma₂ refl refl = sym lemma₁ lemma₃ : {ys : ⟦ List ⟧ A} → (length (to xs) , P.lookup (to xs)) ≡ ys → _≡_ {A = ⟦ List ⟧ A} (suc (length (to xs)) , P.lookup (P._∷_ x (to xs))) (suc (proj₁ ys) , [ (λ _ → x) , proj₂ ys ]) lemma₃ ≡ys = lemma₂ (proj₁ ≡,≡) (proj₂ ≡,≡) where ≡,≡ = _↔_.from Σ-≡,≡↔≡ ≡ys -- The two definitions of Any are isomorphic (both via "to" and -- "from"). Any↔Any-to : {A : Set} (xs : ⟦ List ⟧ A) (P : A → Set) → Any P xs ↔ AnyL P (_⇔_.to List⇔List xs) Any↔Any-to (zero , lkup) P = (∃ λ (p : Fin zero) → P (lkup p)) ↔⟨ ∃-Fin-zero _ ⟩ ⊥ □ Any↔Any-to (suc n , lkup) P = (∃ λ (p : Fin (suc n)) → P (lkup p)) ↔⟨ ∃-Fin-suc _ ⟩ P (lkup (inj₁ tt)) ⊎ Any {C = List} P (n , lkup ∘ inj₂) ↔⟨ id ⊎-cong Any↔Any-to (n , lkup ∘ inj₂) P ⟩ P (lkup (inj₁ tt)) ⊎ AnyL P (_⇔_.to List⇔List (n , lkup ∘ inj₂)) □ Any-from↔Any : {A : Set} (xs : P.List A) (P : A → Set) → Any P (_⇔_.from List⇔List xs) ↔ AnyL P xs Any-from↔Any P.[] P = (∃ λ (p : Fin zero) → P (P.lookup P.[] p)) ↔⟨ ∃-Fin-zero _ ⟩ ⊥ □ Any-from↔Any (P._∷_ x xs) P = (∃ λ (p : Fin (suc (P.length xs))) → P (P.lookup (P._∷_ x xs) p)) ↔⟨ ∃-Fin-suc _ ⟩ P x ⊎ Any {C = List} P (_⇔_.from List⇔List xs) ↔⟨ id ⊎-cong Any-from↔Any xs P ⟩ P x ⊎ AnyL P xs □ -- The definition of bag equivalence in Bag-equivalence and the one in -- Container, instantiated with the List container, are equivalent -- (both via "to" and "from"). ≈-⇔-to-≈-to : {A : Set} {xs ys : ⟦ List ⟧ A} → xs ≈-bag ys ⇔ _⇔_.to List⇔List xs ≈-bagL _⇔_.to List⇔List ys ≈-⇔-to-≈-to {xs = xs} {ys} = record { to = λ xs≈ys z → z ∈L (_⇔_.to List⇔List xs) ↔⟨ inverse $ Any↔Any-to xs _ ⟩ z ∈ xs ↔⟨ xs≈ys z ⟩ z ∈ ys ↔⟨ Any↔Any-to ys _ ⟩ z ∈L (_⇔_.to List⇔List ys) □ ; from = λ xs≈ys z → z ∈ xs ↔⟨ Any↔Any-to xs _ ⟩ z ∈L (_⇔_.to List⇔List xs) ↔⟨ xs≈ys z ⟩ z ∈L (_⇔_.to List⇔List ys) ↔⟨ inverse $ Any↔Any-to ys _ ⟩ z ∈ ys □ } ≈-⇔-from-≈-from : {A : Set} {xs ys : P.List A} → xs ≈-bagL ys ⇔ _⇔_.from List⇔List xs ≈-bag _⇔_.from List⇔List ys ≈-⇔-from-≈-from {xs = xs} {ys} = record { to = λ xs≈ys z → z ∈ (_⇔_.from List⇔List xs) ↔⟨ Any-from↔Any xs _ ⟩ z ∈L xs ↔⟨ xs≈ys z ⟩ z ∈L ys ↔⟨ inverse $ Any-from↔Any ys _ ⟩ z ∈ (_⇔_.from List⇔List ys) □ ; from = λ xs≈ys z → z ∈L xs ↔⟨ inverse $ Any-from↔Any xs _ ⟩ z ∈ (_⇔_.from List⇔List xs) ↔⟨ xs≈ys z ⟩ z ∈ (_⇔_.from List⇔List ys) ↔⟨ Any-from↔Any ys _ ⟩ z ∈L ys □ } ------------------------------------------------------------------------ -- Constructors [] : {A : Set} → ⟦ List ⟧ A [] = (zero , λ ()) infixr 5 _∷_ _∷_ : {A : Set} → A → ⟦ List ⟧ A → ⟦ List ⟧ A x ∷ (n , lkup) = (suc n , [ (λ _ → x) , lkup ]) -- Even if we don't assume extensionality we can prove that -- intensionally distinct implementations of the constructors are bag -- equivalent. []≈ : {A : Set} {lkup : _ → A} → _≈-bag_ {C₂ = List} [] (zero , lkup) []≈ _ = record { surjection = record { equivalence = record { to = λ { (() , _) } ; from = λ { (() , _) } } ; right-inverse-of = λ { (() , _) } } ; left-inverse-of = λ { (() , _) } } ∷≈ : ∀ {A : Set} {n} {lkup : _ → A} → _≈-bag_ {C₂ = List} (lkup (inj₁ tt) ∷ (n , lkup ∘ inj₂)) (suc n , lkup) ∷≈ _ = record { surjection = record { equivalence = record { to = λ { (inj₁ tt , eq) → (inj₁ tt , eq) ; (inj₂ s , eq) → (inj₂ s , eq) } ; from = λ { (inj₁ tt , eq) → (inj₁ tt , eq) ; (inj₂ s , eq) → (inj₂ s , eq) } } ; right-inverse-of = λ { (inj₁ tt , eq) → refl ; (inj₂ s , eq) → refl } } ; left-inverse-of = λ { (inj₁ tt , eq) → refl ; (inj₂ s , eq) → refl } } -- Any lemmas for the constructors. Any-[] : {A : Set} (P : A → Set) → Any P [] ↔ ⊥₀ Any-[] _ = record { surjection = record { equivalence = record { to = λ { (() , _) } ; from = λ () } ; right-inverse-of = λ () } ; left-inverse-of = λ { (() , _) } } Any-∷ : ∀ {A : Set} (P : A → Set) {x xs} → Any P (x ∷ xs) ↔ P x ⊎ Any P xs Any-∷ _ = record { surjection = record { equivalence = record { to = λ { (inj₁ tt , eq) → inj₁ eq ; (inj₂ s , eq) → inj₂ (s , eq) } ; from = λ { (inj₁ eq) → (inj₁ tt , eq) ; (inj₂ (s , eq)) → (inj₂ s , eq) } } ; right-inverse-of = λ { (inj₁ eq) → refl ; (inj₂ (s , eq)) → refl } } ; left-inverse-of = λ { (inj₁ tt , eq) → refl ; (inj₂ s , eq) → refl } } ------------------------------------------------------------------------ -- More functions -- A fold for lists. (Well, this is not a catamorphism, it is a -- paramorphism.) fold : {A B : Set} → B → (A → ⟦ List ⟧ A → B → B) → ⟦ List ⟧ A → B fold nl cns (zero , lkup) = nl fold nl cns (suc n , lkup) = cns (lkup (inj₁ tt)) (n , lkup ∘ inj₂) (fold nl cns (n , lkup ∘ inj₂)) -- A lemma which can be used to prove properties about fold. -- -- The "respects bag equivalence" argument could be omitted if -- equality of functions were extensional. fold-lemma : ∀ {A B : Set} {nl : B} {cns : A → ⟦ List ⟧ A → B → B} (P : ⟦ List ⟧ A → B → Set) → (∀ xs ys → xs ≈-bag ys → ∀ b → P xs b → P ys b) → P [] nl → (∀ x xs b → P xs b → P (x ∷ xs) (cns x xs b)) → ∀ xs → P xs (fold nl cns xs) fold-lemma Q resp nl cns (zero , lkup) = resp _ _ []≈ _ nl fold-lemma Q resp nl cns (suc n , lkup) = resp _ _ ∷≈ _ $ cns _ _ _ $ fold-lemma Q resp nl cns (n , lkup ∘ inj₂) -- Why have I included both fold and fold-lemma rather than simply a -- dependent eliminator? I tried this, and could easily define the -- functions I wanted to define. However, the functions were defined -- together with (partial) correctness proofs, and were unnecessarily -- hard to read. I wanted to be able to define functions which were -- easy to read, like the _++_ function below, and then have the -- option to prove properties about them, like Any-++. -- -- Unfortunately this turned out to be harder than expected. When -- proving the Any-++ lemma it seemed as if I had to prove that _++_ -- preserves bag equivalence in its first argument in order to -- instantiate the "respects bag equivalence" argument. However, my -- preferred proof of this property uses Any-++… -- -- An alternative could be to assume that equality of functions is -- extensional, in which case the "respects bag equivalence" argument -- could be removed. Another option would be to listen to Conor -- McBride and avoid higher-order representations of first-order data. -- Append. infixr 5 _++_ _++_ : {A : Set} → ⟦ List ⟧ A → ⟦ List ⟧ A → ⟦ List ⟧ A xs ++ ys = fold ys (λ z _ zs → z ∷ zs) xs -- An Any lemma for append. Any-++ : ∀ {A : Set} (P : A → Set) xs ys → Any P (xs ++ ys) ↔ Any P xs ⊎ Any P ys Any-++ P xs ys = fold-lemma (λ xs xs++ys → Any P xs++ys ↔ Any P xs ⊎ Any P ys) (λ us vs us≈vs us++ys hyp → Any P us++ys ↔⟨ hyp ⟩ Any P us ⊎ Any P ys ↔⟨ _⇔_.to (∼⇔∼″ us vs) us≈vs P ⊎-cong id ⟩ Any P vs ⊎ Any P ys □) (Any P ys ↔⟨ inverse ⊎-left-identity ⟩ ⊥ ⊎ Any P ys ↔⟨ inverse (Any-[] P) ⊎-cong id ⟩ Any P [] ⊎ Any P ys □) (λ x xs xs++ys ih → Any P (x ∷ xs++ys) ↔⟨ Any-∷ P ⟩ P x ⊎ Any P xs++ys ↔⟨ id ⊎-cong ih ⟩ P x ⊎ Any P xs ⊎ Any P ys ↔⟨ ⊎-assoc ⟩ (P x ⊎ Any P xs) ⊎ Any P ys ↔⟨ inverse (Any-∷ P) ⊎-cong id ⟩ Any P (x ∷ xs) ⊎ Any P ys □) xs