forked from epfl-lara/stainless
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ListMonad.scala
86 lines (72 loc) · 2.57 KB
/
ListMonad.scala
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
import stainless.lang._
import stainless.proof._
import stainless.collection._
object ListMonad {
def append[T](l1: List[T], l2: List[T]): List[T] = {
decreases(l1)
l1 match {
case Cons(head, tail) => Cons(head, append(tail, l2))
case Nil() => l2
}
}.ensuring { res => (res == l1) || (l2 != Nil[T]()) }
def flatMap[T,U](list: List[T], f: T => List[U]): List[U] = {
decreases(list)
list match {
case Cons(head, tail) => append(f(head), flatMap(tail, f))
case Nil() => Nil()
}
}
def associative_lemma[T,U,V](list: List[T], f: T => List[U], g: U => List[V]): Boolean = {
flatMap(flatMap(list, f), g) == flatMap(list, (x: T) => flatMap(f(x), g))
}
def associative_lemma_induct[T,U,V](list: List[T], flist: List[U], glist: List[V], f: T => List[U], g: U => List[V]): Boolean = {
decreases(list, flist, glist)
associative_lemma(list, f, g).because {
(append(glist, flatMap(append(flist, flatMap(list, f)), g)) == append(append(glist, flatMap(flist, g)), flatMap(list, (x: T) => flatMap(f(x), g)))).because(
(glist match {
case Cons(ghead, gtail) =>
associative_lemma_induct(list, flist, gtail, f, g)
case Nil() => flist match {
case Cons(fhead, ftail) =>
associative_lemma_induct(list, ftail, g(fhead), f, g)
case Nil() => list match {
case Cons(head, tail) => associative_lemma_induct(tail, f(head), Nil(), f, g)
case Nil() => true
}
}
}))
}
}.holds
def left_unit_law[T,U](x: T, f: T => List[U]): Boolean = {
flatMap(Cons(x, Nil()), f) == f(x)
}.holds
def right_unit_law[T](list: List[T]): Boolean = {
flatMap(list, (x: T) => Cons(x, Nil[T]())) == list
}
def right_unit_induct[T](list: List[T]): Boolean = {
decreases(list)
right_unit_law(list).because((list match {
case Cons(head, tail) => right_unit_induct(tail)
case Nil() => true
}))
}.holds
def flatMap_zero_law[T,U](f: T => List[U]): Boolean = {
flatMap(Nil[T](), f) == Nil[U]()
}.holds
def flatMap_to_zero_law[T](list: List[T]): Boolean = {
flatMap(list, (x: T) => Nil[T]()) == Nil[T]()
}
def flatMap_to_zero_induct[T](list: List[T]): Boolean = {
decreases(list)
flatMap_to_zero_law(list).because((list match {
case Cons(head, tail) => flatMap_to_zero_induct(tail)
case Nil() => true
}))
}.holds
def add_zero_law[T](list: List[T]): Boolean = {
append(list, Nil()) == list
}.holds
def zero_add_law[T](list: List[T]): Boolean = {
append(Nil(), list) == list
}.holds
}