Even and Odd data types

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 , )
  )  []