hpastetwo

Coinduction on the Conaturals

author
dolio
age
311 days
language
haskell
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
module Conat where

infixl 80 __
__ : {A B C}  (B  C)  (A  B)  (A  C)
(g  f) x = g (f x)

-- The equality type
infix 40 __
data __ {A : Set} (x : A) : A  Set where
  refl : x  x

-symm : {A} {x y : A}  x  y  y  x
-symm refl = refl

-trans : {A} {x y z : A}  x  y  y  z  x  z
-trans refl refl = refl

-ap : {A B : Set} {f g : A  B}  f  g  (x : A)  f x  g x
-ap refl _ = refl

-- The empty/false/initial type
data  : Set where

-- The singleton/true/final type
record  : Set where

-- sums
data __ (A B : Set) : Set where
  inl : A  A  B
  inr : B  A  B

-- dependent sum, and a couple variations thereof that we won't use
infixr 50 _,_
data Σ (A : Set) (P : A  Set) : Set where
  _,_ : (x : A) (w : P x)  Σ A P

infixr 45 _×_
_×_ : Set  Set  Set
A × B = Σ A (λ _  B)

 : {A : Set} (P : A  Set)  Set
 {A} P = Σ A P

-- The only coinductive definition we'll use. Codata is still
-- having the kinks worked out of it in Agda (as far as practical
-- presentation of the underlying theory goes), and the current
-- recommended discipline is to make a single codata declaration
-- like the one below, and then encode νF as (something like):
--
--   data νF' : Set where in : F (∞ νF') → νF'
--   
--   νF : Set
--   νF = ∞ νF'
--
-- although one typically inlines F into the data definition,
-- and doesn't bother with the outermost ∞ (though I will here
-- because it's technically correct).
infix 30 _
codata _ (A : Set) : Set where
  _ : A   A

-- observes come codata
 : {A}   A  A
 ( x) = x

-- Extended naturals, adhering to the above discipline
data Conat : Set where
  cozero : Conat
  cosuc  :  Conat  Conat

Co : Set
Co =  Conat

-- some pseudo-constructors for Coℕ
zero : Co
zero =  cozero

suc : Co  Co
suc m =  (cosuc m)

-- The coalgebra mophism guaranteed by the finality of Coℕ.
-- We can't use suc, because that doesn't satisfy the guardedness
-- criterion of the productivity checker.
unfold : {A}  (A    A)  A  Co
unfold step seed with step seed
... | inl _     = zero
... | inr seed' =  cosuc (unfold step seed')

-- The relevant map for the Coℕ functor
map : {A B}  (A  B)    A    B
map f (inl _) = inl _
map f (inr y) = inr (f y)

-- The coalgebra action for Coℕ
observe : Co    Co
observe cn with  cn
... | cozero    = inl _
... | cosuc cn' = inr cn'

postulate
  -- Agda doesn't have extensional equality, so technically codata
  -- is only provably weakly final (existence but not uniqueness).
  -- Since that's no fun, I'll take the uniqueness part of finality
  -- as a postulate and use it to prove extensional equality for Coℕ.
  Co! : {A}  (step : A    A)        -- given a coalgebra
               (g : A  Co)              -- coalgebra morphisms to
               observe  g  map g  step --  the final coalgebra
               g  unfold step            --  are unique

  -- Extensional equality on functions also comes in handy. I don't
  -- think I can do without it here, but I may just be lazy.
  extensionality : {A B : Set} {f g : A  B}  ( x  f x  g x)  f  g

-- m ~ n is a bisimulation of m and n
infix 40 _~_
data _~_ : Conat  Conat  Set where
  cozero : cozero ~ cozero
  cosuc  : {m n}   ( m ~  n)  cosuc m ~ cosuc n

-- Show that _~_ is an equivalence relation, for kicks.
~-refl :  m   m ~  m
~-refl m with  m
... | cozero   = cozero
... | cosuc m' = cosuc ( ~-refl m')

~-symm :  m n   m ~  n   n ~  m
~-symm m n m~n with  m |  n
~-symm m n cozero      | cozero   | cozero   = cozero
~-symm m n ()          | cozero   | cosuc _
~-symm m n ()          | cosuc _  | cozero
~-symm m n (cosuc m~n) | cosuc m' | cosuc n' = cosuc ( ~-symm m' n' ( m~n))

~-trans :  m n o   m ~  n   n ~  o   m ~  o
~-trans m n o m~n n~o with  m |  n |  o
~-trans m n o m~n n~o                 | cozero   | cozero   | cozero   = cozero
~-trans m n o m~n ()                  | cozero   | cozero   | cosuc _
~-trans m n o () n~o                  | cozero   | cosuc n' | fo
~-trans m n o () n~o                  | cosuc m' | cozero   | fo
~-trans m n o m~n ()                  | cosuc m' | cosuc n' | cozero
~-trans m n o (cosuc m~n) (cosuc n~o) | cosuc m' | cosuc n' | cosuc o'
   = cosuc ( ~-trans m' n' o' ( m~n) ( n~o))

-- Bisim is the type of bisimulations on Coℕ. Showing
-- that this type is a ⊤ ⊎ ─ coalgebra is how we prove
-- extensional equality.
Bisim : Set
Bisim = Σ Co λ m  Σ Co λ n   ( m ~  n)

-- Here are two candidate coalgebra morphisms from Bisim to Coℕ
b-π₁ : Bisim  Co
b-π₁ (m , _) = m

b-π₂ : Bisim  Co
b-π₂ (_ , n , _) = n

-- This is a coalgebra action on Bisim
b-step : Bisim    Bisim
b-step (cm , cn , ext) with  cm |  cn |  ext
... | .cozero    | .cozero    | cozero = inl _
... | .(cosuc m) | .(cosuc n) | cosuc {m} {n} ext' = inr (m , n , ext')

-- What do you know, b-π₁ and b-π₂ are, in fact, coalgebra
-- morphisms
eq : observe  b-π₁  map b-π₁  b-step
eq = extensionality eq-aux
 where
 eq-aux :  bi  observe (b-π₁ bi)  map b-π₁ (b-step bi)
 eq-aux (cm , cn , ext) with  cm |  cn |  ext
 ... | .cozero    | .cozero    | cozero             = refl
 ... | .(cosuc m) | .(cosuc n) | cosuc {m} {n} ext' = refl

eq : observe  b-π₂  map b-π₂  b-step
eq = extensionality eq-aux
 where
 eq-aux :  bi  observe (b-π₂ bi)  map b-π₂ (b-step bi)
 eq-aux (cm , cn , ext) with  cm |  cn |  ext
 ... | .cozero    | .cozero    | cozero             = refl
 ... | .(cosuc m) | .(cosuc n) | cosuc {m} {n} ext' = refl

-- Since they are, we can prove that they are equal (by
-- coinduction/finality), and thus applying them a single value
-- yields equal values. This allows us to prove the equality of
-- any two Coℕs for which we have a bisimulation. This is
-- extensional, as we have shown that two values are equal if
-- they produce the same sequence of observations.
bisim- : {m n}   ( m ~  n)  m  n
bisim- {m} {n} ext = -ap lemma (m , n , ext)
 where
 lemma : b-π₁  unfold b-step
 lemma = Co! b-step b-π₁ eq

 lemma : b-π₂  unfold b-step
 lemma = Co! b-step b-π₂ eq

 lemma : b-π₁  b-π₂
 lemma = -trans lemma (-symm lemma)

-- Extensional equality is pretty nice, because intensional
-- equality (the built-in kind in Agda, and all you'll normally
-- get out of _≡_ and such) doesn't even buy you the fact
-- that i and j below are equal (not that it should; you can
-- prove it in Coq (and could in the original codata implementation
-- Agda had), and that causes problems).
i : Co
i =  (cosuc i)

j : Co
j =  (cosuc i)

simple : i  j
simple = bisim- ( cosuc ( ~-refl i))