Definition of the mutually recursive data types Even and Odd.
module even-odd where open import map open import phantomNat using (phantomZero[] ; phantomSuc[_] ; ⊢phantomZero ; ⊢phantomSuc) open import Data.List using (_∷_ ; [] ; [_] ) open import Data.Vec renaming (_∷_ to _¦_ ; [] to [||] ; [_] to [|_|]) open import Data.Unit renaming (tt to ∗) open import Data.Product using (_,_)We define the variables and contexts used in the rest of the file.
α β : mix-var
α = tvar 0
β = tvar 1
Γα Γβ : context {mix-var}
Γα = ⟦ [| α |] ⟧
Γβ = ⟦ [| β |] ⟧
We define now the data types Even and Odd as they usually appear in the literature. Even has two constructors, one proving that phantomZero is even, and one producing a proof that phantomSuc n is even from a proof that n is odd. Odd has a unique constructor producing a proof that phantomSuc n is odd from a proof that n is even.
Even-id Odd-id : id-TC-var Even-id = 8 Odd-id = 9 Even-name Odd-name : TC-var 1 Even-name = TC Even-id 1 Odd-name = TC Odd-id 1 even-zero-id even-suc-id odd-suc-id : id-data-constructor even-zero-id = 80 even-suc-id = 81 odd-suc-id = 90 even-zero even-suc odd-suc : data-constructor 1 even-zero = even-zero-id ∷ [] ⇒ᵀ [| phantomZero[] |] even-suc = even-suc-id ∷ [ appᵀ Odd-name [ [| varᵀ α |] ] ] ⇒ᵀ [| phantomSuc[ varᵀ α ] |] odd-suc = odd-suc-id ∷ [ appᵀ Even-name [ [| varᵀ β |] ] ] ⇒ᵀ [| phantomSuc[ varᵀ β ] |] Even Odd : user-defined 1 Even = dataᵀ Even-name whereᵀ (even-zero ∷ even-suc ∷ []) Odd = dataᵀ Odd-name whereᵀ [ odd-suc ] Even[_] = λ n → data-typeᵀ Even [ [| n |] ] Odd[_] = λ n → data-typeᵀ Odd [ [| n |] ] even-zero[] = dc even-zero [||] even-suc[_] = λ x → dc even-suc [| x |] odd-suc[_] = λ x → dc odd-suc [| x |]
Here are the justifications for Odd and Even. Notice the need for Odd-name in the TC context of the justification of even-suc, and, conversely, the need for Even-name in the TC context of the justification of odd-suc. This is where the mutual recursive definition manifests.
⊢even-zero : ∅ ∥ ∅ ⊢ᶜ even-zero
⊢even-zero = dcᵀ-intro {G = Even-name} ∗ (⊢phantomZero , ∗)
⊢even-suc : Γα ∥ (∅ , (_ , Odd-name)) ⊢ᶜ even-suc
⊢even-suc = dcᵀ-intro {G = Even-name} (app-intro (var-intro ∋-last , ∗) (∋-rec ∋-last) , ∗) (⊢phantomSuc (var-intro ∋-last , ∗) , ∗)
⊢odd-suc : Γβ ∥ (∅ , (_ , Even-name)) ⊢ᶜ odd-suc
⊢odd-suc = dcᵀ-intro {G = Odd-name} ((app-intro (var-intro ∋-last , ∗) (∋-rec ∋-last) , ∗)) (⊢phantomSuc (var-intro ∋-last , ∗) , ∗)
⊢Even : ∀ {Γ χ φ} → _ → Γ ∥ χ ⊢ Even[ φ ]
⊢Even ⊢φ =
data-intro {Γcs = ∅ ∷ Γα ∷ []} {χcs = ∅ ∷ (∅ , (_ , Odd-name)) ∷ []}
(⊢even-zero , ⊢even-suc , ∗) (⊢φ , ∗)
⊢Odd : ∀ {Γ χ φ} → _ → Γ ∥ χ ⊢ Odd[ φ ]
⊢Odd ⊢φ =
data-intro {Γcs = Γβ ∷ []} {χcs = (∅ , (_ , Even-name)) ∷ []}
(⊢odd-suc , ∗) (⊢φ , ∗)
By comparison with calls to adm on terms of non-mutually-recursive data types, a call to adm on a term in Even or in Odd will need to carry a non-empty stack of already-encountered data types. Indeed, if the call is made with an empty stack on a term of the form even-suc p, the algorithm will fail to recognize Odd at the known data type when examining the type of p. For that reason, we define the following ready-to-use stack:
Even∷Odd∷[] =
(
arity _
def Even
ctx (∅ ∷ Γα ∷ []) ∥ (∅ ∷ (∅ , (_ , Odd-name)) ∷ [])
der (⊢even-zero , ⊢even-suc , ∗)
) ∷ (
arity _
def Odd
ctx (Γβ ∷ []) ∥ ((∅ , (_ , Even-name)) ∷ [])
der (⊢odd-suc , ∗)
) ∷ []