-
Notifications
You must be signed in to change notification settings - Fork 8
/
stream.ml
60 lines (50 loc) · 2.95 KB
/
stream.ml
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
open Testutil.Repl
let () =
run @@ fun () ->
def "Stream" "Type → Type" "A ↦ codata [ _ .head : A | _ .tail : Stream A ]";
assume "A" "Type";
(* We suppose a stream and take some parts of it *)
assume "s" "Stream A";
def "s0" "A" "s .head";
def "s0t" "Stream A" "s .tail";
def "s1" "A" "s .tail .head";
def "s2" "A" "s .tail .tail .head";
(* With a non-corecursive copattern-match, we can define a one-step eta-expansion, and see that streams don't satisfy eta-conversion. *)
def "s'" "Stream A" "[ .head ↦ s .head | .tail ↦ s .tail ]";
unequal_at "s" "s'" "Stream A";
(* Their identity/bridge types are bisimulations. We can use this to prove propositional one-step eta-conversion. *)
def "s_is_s'" "Id (Stream A) s s'" "[ .head ↦ refl (s .head) | .tail ↦ refl (s .tail) ]";
(* We can also define the general corecursor 'corec' by copattern-matching. *)
def "corec" "(A X : Type) → (X → A) → (X → X) → X → Stream A"
"A X h t x ↦ [ .head ↦ h x | .tail ↦ corec A X h t (t x) ]";
(* Using corec, we can also define an infinitary eta-expansion. *)
def "s''" "Stream A" "corec A (Stream A) (x ↦ x .head) (x ↦ x .tail) s";
unequal_at "s" "s''" "Stream A";
(* We can't prove this with a non-corecursive copattern-match (since it's more than one step) or with "refl corec" (since it can only equate two values of corec). We need a more general bisimulation, which must be defined by a full coinduction, which in turn requires a new operation defined by a higher-dimensional copattern case tree. *)
def "∞eta" "Stream A → Stream A" "s ↦ [ .head ↦ s .head | .tail ↦ ∞eta (s .tail) ]";
unequal_at "s ↦ s" "s ↦ ∞eta s" "Stream A → Stream A";
def "∞eta_bisim" "(s:Stream A) → Id (Stream A) s (∞eta s)"
"s ↦ [ .head ↦ refl (s .head) | .tail ↦ ∞eta_bisim (s .tail) ]";
(* We construct a stream of natural numbers and check its first few elements *)
def "ℕ" "Type" "data [ zero. | suc. (_ : ℕ) ]";
def "nats" "Stream ℕ" "corec ℕ ℕ (x ↦ x) (x ↦ suc. x) 0";
equal_at "nats .head" "0" "ℕ";
equal_at "nats .tail .head" "1" "ℕ";
equal_at "nats .tail .tail .head" "2" "ℕ";
equal_at "nats .tail .tail .tail .head" "3" "ℕ";
(* Now we construct the stream of fibonacci numbers and check the first few of its elements *)
def "plus" "ℕ → ℕ → ℕ"
"m n ↦ match n [
| zero. ↦ m
| suc. n ↦ suc. (plus m n)
]";
def "prod" "Type → Type → Type" "A B ↦ sig (fst : A, snd : B)";
def "fib" "Stream ℕ"
"corec ℕ (prod ℕ ℕ) (x ↦ x .fst) (x ↦ ( fst ≔ x .snd , snd ≔ plus (x .fst) (x .snd) )) (fst ≔ 1, snd ≔ 1)";
equal_at "fib .head" "1" "ℕ";
equal_at "fib .tail .head" "1" "ℕ";
equal_at "fib .tail .tail .head" "2" "ℕ";
equal_at "fib .tail .tail .tail .head" "3" "ℕ";
equal_at "fib .tail .tail .tail .tail .head" "5" "ℕ";
equal_at "fib .tail .tail .tail .tail .tail .head" "8" "ℕ";
()