Skip to content

Commit 2bbb0d9

Browse files
committed
Add extra examples from paper
1 parent 6d8f4c3 commit 2bbb0d9

File tree

2 files changed

+211
-0
lines changed

2 files changed

+211
-0
lines changed

tests/run/fully-abstract-nat-5.check

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CaseNums
2+
ok
3+
ok
4+
ok - unchecked error
5+
None
6+
Some((SuccClass(ZeroObj),SuccClass(ZeroObj)))
7+
None
8+
Some((SuccClass(ZeroObj),SuccClass(ZeroObj)))
9+
10+
IntNums
11+
ok
12+
ok
13+
ok - unchecked error
14+
None
15+
Some((1,1))
16+
None
17+
Some((1,1))

tests/run/fully-abstract-nat-5.scala

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
println("CaseNums")
5+
test(CaseNums)
6+
println()
7+
println("IntNums")
8+
test(IntNums)
9+
}
10+
11+
def test(numbers: Numbers) = {
12+
import numbers._
13+
14+
val zero: Nat = Zero()
15+
val one: Nat = Succ(zero)
16+
val two: Nat = Succ(one)
17+
val three: Nat = Succ(two)
18+
19+
zero match {
20+
case Succ(p) => println("error")
21+
case Zero(_) => println("ok") // extra argument removed by language extension
22+
}
23+
24+
one match {
25+
case Zero(_) => println("error") // extra argument removed by language extension
26+
case Succ(p) => println("ok")
27+
}
28+
29+
zero match {
30+
case s: Succ => println("ok - unchecked error")
31+
case z: Zero => println("ok - unchecked no error")
32+
}
33+
34+
def divOpt(a: Nat, b: Nat): Option[(Nat, Nat)] = b match {
35+
case s @ Succ(p) =>
36+
Some(safeDiv(a, s.asInstanceOf[Succ])) // cast should not be needed with extension
37+
case _ => None
38+
}
39+
println(divOpt(one, zero))
40+
println(divOpt(three, two))
41+
42+
def divOptExpanded(a: Nat, b: Nat): Option[(Nat, Nat)] = {
43+
val x0 = Succ.unapply(b)
44+
if (!x0.isEmpty) {
45+
val s = x0.refined
46+
val p = x0.get
47+
Some(safeDiv(a, s))
48+
} else {
49+
None
50+
}
51+
}
52+
println(divOptExpanded(one, zero))
53+
println(divOptExpanded(three, two))
54+
}
55+
}
56+
57+
trait Numbers {
58+
59+
type Nat
60+
type Zero <: Nat
61+
type Succ <: Nat
62+
63+
val Zero: ZeroExtractor
64+
trait ZeroExtractor {
65+
def apply(): Zero
66+
def unapply(nat: Nat): ZeroOpt // check that ZeroOpt#Refined <: Nat
67+
}
68+
trait ZeroOpt {
69+
type Refined = Zero // optionally added by language extension
70+
def get: Null // Language extension should remove this
71+
def isEmpty: Boolean
72+
}
73+
74+
val Succ: SuccExtractor
75+
trait SuccExtractor {
76+
def apply(nat: Nat): Succ
77+
def unapply(nat: Nat): SuccOpt { type Refined <: nat.type } // type could be inserted by the compiler if Refined is declared
78+
}
79+
trait SuccOpt {
80+
type Refined <: Singleton
81+
def refined: Refined & Succ // optionally added by language extension
82+
def get: Nat
83+
def isEmpty: Boolean
84+
}
85+
86+
implicit def SuccDeco(succ: Succ): SuccAPI
87+
trait SuccAPI {
88+
def pred: Nat
89+
}
90+
91+
def safeDiv(a: Nat, b: Succ): (Nat, Nat)
92+
}
93+
94+
object CaseNums extends Numbers {
95+
96+
trait NatClass
97+
case object ZeroObj extends NatClass with ZeroOpt {
98+
def get: Null = null // Should be removed by language extension
99+
def isEmpty: Boolean = false
100+
type Refined = this.type
101+
def refined = this
102+
}
103+
case class SuccClass(pred: NatClass) extends NatClass with SuccOpt {
104+
def get: NatClass = pred
105+
def isEmpty: Boolean = false
106+
type Refined = this.type
107+
def refined = this
108+
}
109+
110+
class EmptyZeroOpt extends ZeroOpt {
111+
def isEmpty: Boolean = true
112+
def get: Null = throw new Exception("empty")
113+
}
114+
115+
class EmptySuccOpt extends SuccOpt {
116+
def isEmpty: Boolean = true
117+
def get: NatClass = throw new Exception("empty")
118+
type Refined = Nothing
119+
def refined = throw new Exception("empty")
120+
}
121+
122+
type Nat = NatClass
123+
type Zero = ZeroObj.type
124+
type Succ = SuccClass
125+
126+
object Zero extends ZeroExtractor {
127+
def apply(): Zero = ZeroObj
128+
def unapply(nat: Nat): ZeroOpt =
129+
if (nat == ZeroObj) ZeroObj
130+
else new EmptyZeroOpt
131+
}
132+
133+
object Succ extends SuccExtractor {
134+
def apply(nat: Nat): Succ = SuccClass(nat)
135+
def unapply(nat: Nat) = nat match {
136+
case succ: SuccClass => succ.asInstanceOf[nat.type & SuccClass] // cast needed? looks like the type of succ was widden
137+
case _ => new EmptySuccOpt
138+
}
139+
}
140+
141+
def SuccDeco(succ: Succ): SuccAPI = new SuccAPI {
142+
def pred: Nat = succ.pred
143+
}
144+
145+
def safeDiv(a: Nat, b: Succ): (Nat, Nat) = {
146+
def sdiv(div: Nat, rem: Nat): (Nat, Nat) =
147+
if (lessOrEq(rem, b)) (div, rem)
148+
else sdiv(Succ(div), minus(rem, b))
149+
sdiv(Zero(), a)
150+
}
151+
152+
private def lessOrEq(a: Nat, b: Nat): Boolean = (a, b) match {
153+
case (Succ(a1), Succ(b1)) => lessOrEq(a1, b1)
154+
case (Zero(_), _) => true // extra argument removed by language extension
155+
case _ => false
156+
}
157+
158+
// assumes a >= b
159+
private def minus(a: Nat, b: Nat): Nat = (a, b) match {
160+
case (Succ(a1), Succ(b1)) => minus(a1, b1)
161+
case _ => a
162+
}
163+
164+
}
165+
166+
object IntNums extends Numbers {
167+
type Nat = Int
168+
type Zero = Int // 0
169+
type Succ = Int // n > 0
170+
171+
object Zero extends ZeroExtractor {
172+
def apply(): Int = 0
173+
def unapply(nat: Nat): ZeroOpt = new ZeroOpt {
174+
def isEmpty: Boolean = nat != 0
175+
def get: Null = null // language extension will remove this
176+
}
177+
}
178+
179+
object Succ extends SuccExtractor {
180+
def apply(nat: Nat): Int = nat + 1
181+
def unapply(nat: Nat) = new SuccOpt {
182+
def isEmpty: Boolean = nat <= 0
183+
def get: Int = nat - 1
184+
type Refined = nat.type
185+
def refined = nat
186+
}
187+
}
188+
189+
def SuccDeco(succ: Succ): SuccAPI = new SuccAPI {
190+
def pred: Int = succ - 1
191+
}
192+
193+
def safeDiv(a: Nat, b: Succ): (Nat, Nat) = (a / b, a % b)
194+
}

0 commit comments

Comments
 (0)