Skip to content

Commit 264cbf8

Browse files
authored
Merge pull request #4610 from dotty-staging/fully-abstract-interface
Add an implementation example of fully abstract interface
2 parents 4821e2b + 6655a6c commit 264cbf8

18 files changed

+1965
-0
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
CaseClassImplementation
2+
underlying rep: class CaseClassImplementation$Const
3+
1
4+
test1 OK
5+
1 = 1
6+
test2 OK
7+
1 = 1
8+
9+
underlying rep: class CaseClassImplementation$App
10+
7
11+
test3 OK
12+
AppliedOp(PlusOp, Const(1), App(MultOp,Const(2),Const(3))) = 7
13+
test4 OK
14+
AppliedOp(PlusOp, Const(1), App(MultOp,Const(2),Const(3))) = 7
15+
16+
ListImplementation
17+
underlying rep: class scala.collection.immutable.$colon$colon
18+
1
19+
test1 OK
20+
1 = 1
21+
test2 OK
22+
1 = 1
23+
24+
underlying rep: class scala.collection.immutable.$colon$colon
25+
7
26+
test3 OK
27+
AppliedOp(List(+), List(1), List(List(*), List(2), List(3))) = 7
28+
test4 OK
29+
AppliedOp(List(+), List(1), List(List(*), List(2), List(3))) = 7
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
import scala.reflect.ClassTag
2+
3+
object Test {
4+
def main(args: Array[String]): Unit = {
5+
println("CaseClassImplementation")
6+
testInterface(CaseClassImplementation)
7+
8+
println()
9+
10+
println("ListImplementation")
11+
testInterface(ListImplementation)
12+
}
13+
14+
def testInterface(arithmetic: Arithmetic): Unit = {
15+
import arithmetic._
16+
val const1 = Constant(1)
17+
println("underlying rep: " + const1.getClass)
18+
println(const1.eval)
19+
20+
const1 match {
21+
case AppliedOp(_, _, _) =>
22+
println("test1 fail")
23+
case c @ Constant(n) =>
24+
println("test1 OK")
25+
println(s"$n = ${c.eval}")
26+
}
27+
28+
const1 match {
29+
case _: AppliedOp =>
30+
println("test2 fail")
31+
case c: Constant =>
32+
println("test2 OK")
33+
println(s"${c.num} = ${c.eval}")
34+
}
35+
println()
36+
37+
// 1 + (2 * 3)
38+
val applied = AppliedOp(Op.Puls(), Constant(1), AppliedOp(Op.Mult(), Constant(2), Constant(3)))
39+
40+
println("underlying rep: " + applied.getClass)
41+
println(applied.eval)
42+
43+
applied match {
44+
case c @ Constant(n) =>
45+
println("test3 fail")
46+
case a @ AppliedOp(op, x, y) =>
47+
println("test3 OK")
48+
println(s"AppliedOp($op, $x, $y) = ${a.eval}")
49+
}
50+
51+
applied match {
52+
case c: Constant =>
53+
println("test4 fail")
54+
case a: AppliedOp =>
55+
println("test4 OK")
56+
println(s"AppliedOp(${a.op}, ${a.lhs}, ${a.rhs}) = ${a.eval}")
57+
}
58+
59+
}
60+
}
61+
62+
abstract class Arithmetic {
63+
64+
// === Numbers ==========================================
65+
// Represents:
66+
// trait Number
67+
// case class Constant(n: Int) extends Number
68+
// case class AppliedOp(op: Op, lhs: Number, rhs: Number) extends Number
69+
70+
type Number
71+
implicit def numberClassTag: ClassTag[Number]
72+
73+
trait AbstractNumber {
74+
def thisNumber: Number
75+
def eval: Int = thisNumber match {
76+
case Constant(n) => n
77+
case AppliedOp(op, x, y) => op(x, y)
78+
}
79+
}
80+
implicit def NumberDeco(t: Number): AbstractNumber
81+
82+
// --- Constant ----------------------------------------
83+
84+
type Constant <: Number
85+
implicit def constantClassTag: ClassTag[Constant]
86+
87+
val Constant: ConstantExtractor
88+
abstract class ConstantExtractor {
89+
def apply(x: Int): Constant
90+
def unapply(x: Constant): Option[Int]
91+
}
92+
trait AbstractConstant {
93+
def num: Int
94+
}
95+
implicit def ConstantDeco(t: Constant): AbstractConstant
96+
97+
// --- AppliedOp ----------------------------------------
98+
99+
type AppliedOp <: Number
100+
implicit def appliedOpClassTag: ClassTag[AppliedOp]
101+
102+
trait AbstractAppliedOp {
103+
def op: Op
104+
def lhs: Number
105+
def rhs: Number
106+
}
107+
implicit def AppliedOpDeco(t: AppliedOp): AbstractAppliedOp
108+
109+
val AppliedOp: AppliedOpExtractor
110+
abstract class AppliedOpExtractor {
111+
def apply(op: Op, x: Number, y: Number): AppliedOp
112+
def unapply(x: AppliedOp): Option[(Op, Number, Number)]
113+
}
114+
115+
// === Operations =======================================
116+
// Represents:
117+
// trait Op
118+
// case object Puls extends Op
119+
// case object Mult extends Op
120+
121+
type Op
122+
implicit def opClassTag: ClassTag[Op]
123+
124+
trait AbstractOp {
125+
def thisOp: Op
126+
def apply(x: Number, y: Number): Int = thisOp match {
127+
case Op.Puls() => x.eval + y.eval
128+
case Op.Mult() => x.eval * y.eval
129+
}
130+
}
131+
implicit def OpDeco(t: Op): AbstractOp
132+
133+
val Op: OpModule
134+
abstract class OpModule {
135+
val Puls: PulsExtractor
136+
abstract class PulsExtractor {
137+
def apply(): Op
138+
def unapply(x: Op): Boolean
139+
}
140+
141+
val Mult: MultExtractor
142+
abstract class MultExtractor {
143+
def apply(): Op
144+
def unapply(x: Op): Boolean
145+
}
146+
}
147+
}
148+
149+
object CaseClassImplementation extends Arithmetic {
150+
151+
// === Numbers ==========================================
152+
// Represented as case classes
153+
154+
sealed trait Num
155+
final case class Const(n: Int) extends Num
156+
final case class App(op: Op, x: Num, y: Num) extends Num
157+
158+
type Number = Num
159+
160+
def numberClassTag: ClassTag[Number] = implicitly
161+
162+
def NumberDeco(t: Number): AbstractNumber = new AbstractNumber {
163+
def thisNumber: Number = t
164+
}
165+
166+
// --- Constant ----------------------------------------
167+
168+
type Constant = Const
169+
def constantClassTag: ClassTag[Constant] = implicitly
170+
171+
def ConstantDeco(const: Constant): AbstractConstant = new AbstractConstant {
172+
def num: Int = const.n
173+
}
174+
175+
object Constant extends ConstantExtractor {
176+
def apply(x: Int): Constant = Const(x)
177+
def unapply(x: Constant): Option[Int] = Some(x.n)
178+
}
179+
180+
// --- AppliedOp ----------------------------------------
181+
182+
def AppliedOpDeco(t: AppliedOp): AbstractAppliedOp = new AbstractAppliedOp {
183+
def op: Op = t.op
184+
def lhs: Number = t.x
185+
def rhs: Number = t.y
186+
}
187+
188+
type AppliedOp = App
189+
def appliedOpClassTag: ClassTag[AppliedOp] = implicitly
190+
191+
object AppliedOp extends AppliedOpExtractor {
192+
def apply(op: Op, x: Number, y: Number): AppliedOp = App(op, x, y)
193+
def unapply(app: AppliedOp): Option[(Op, Number, Number)] = Some((app.op, app.x, app.y))
194+
}
195+
196+
// === Operations =======================================
197+
// Represented as case classes
198+
199+
sealed trait Operation
200+
case object PlusOp extends Operation
201+
case object MultOp extends Operation
202+
203+
type Op = Operation
204+
def opClassTag: ClassTag[Op] = implicitly
205+
206+
def OpDeco(t: Op): AbstractOp = new AbstractOp {
207+
def thisOp: Op = t
208+
}
209+
210+
object Op extends OpModule {
211+
object Puls extends PulsExtractor {
212+
def apply(): Op = PlusOp
213+
def unapply(x: Op): Boolean = x == PlusOp
214+
}
215+
object Mult extends MultExtractor {
216+
def apply(): Op = MultOp
217+
def unapply(x: Op): Boolean = x == MultOp
218+
}
219+
}
220+
}
221+
222+
object ListImplementation extends Arithmetic {
223+
// Logically represented as:
224+
// type Number <: List[Any]
225+
// type Constant <: Number // List(n: Int)
226+
// type AppliedOp <: Number // List(op: Op, lhs: Number, rhs: Number)
227+
//
228+
// type Op <: List[Any] // List(id: "+" | "*")
229+
230+
// === Numbers ==========================================
231+
232+
type Number = List[Any]
233+
234+
def numberClassTag: ClassTag[Number] = new ClassTag[Number] {
235+
def runtimeClass: Class[_] = classOf[List[_]]
236+
override def unapply(x: Any): Option[List[Any]] = x match {
237+
case ls: List[Any] if ls.length == 3 || (ls.length == 1 && ls(0).isInstanceOf[Int]) =>
238+
// Test that it is one of:
239+
// type Constant <: Number // List(n: Int)
240+
// type AppliedOp <: Number // List(op: Op, lhs: Number, rhs: Number)
241+
Some(ls)
242+
case _ => None
243+
}
244+
}
245+
246+
def NumberDeco(t: Number): AbstractNumber = new AbstractNumber {
247+
def thisNumber: Number = t
248+
}
249+
250+
// --- Constant ----------------------------------------
251+
252+
type Constant = List[Any] // List(n: Int)
253+
def constantClassTag: ClassTag[Constant] = new ClassTag[Constant] {
254+
def runtimeClass: Class[_] = classOf[List[_]]
255+
override def unapply(x: Any): Option[List[Any]] = x match {
256+
case ls: List[Any] if ls.length == 1 && ls(0).isInstanceOf[Int] =>
257+
// Test that it is:
258+
// type Constant <: Number // List(n: Int)
259+
Some(ls)
260+
case _ => None
261+
}
262+
}
263+
264+
def ConstantDeco(const: Constant): AbstractConstant = new AbstractConstant {
265+
def num: Int = const(0).asInstanceOf[Int]
266+
}
267+
268+
object Constant extends ConstantExtractor {
269+
def apply(x: Int): Constant = List(x)
270+
def unapply(x: Constant): Option[Int] = Some(ConstantDeco(x).num)
271+
}
272+
273+
// --- AppliedOp ----------------------------------------
274+
275+
def AppliedOpDeco(t: AppliedOp): AbstractAppliedOp = new AbstractAppliedOp {
276+
def op: Op = t(0).asInstanceOf[Op]
277+
def lhs: Number = t(1).asInstanceOf[Number]
278+
def rhs: Number = t(2).asInstanceOf[Number]
279+
}
280+
281+
type AppliedOp = List[Any] // List(op: Op, lhs: Number, rhs: Number)
282+
def appliedOpClassTag: ClassTag[AppliedOp] = new ClassTag[AppliedOp] {
283+
def runtimeClass: Class[_] = classOf[List[_]]
284+
override def unapply(x: Any): Option[List[Any]] = x match {
285+
case ls: List[Any] if ls.length == 3 =>
286+
// Test that it is:
287+
// type AppliedOp <: Number // List(op: Op, lhs: Number, rhs: Number)
288+
Some(ls)
289+
case _ => None
290+
}
291+
}
292+
293+
object AppliedOp extends AppliedOpExtractor {
294+
def apply(op: Op, x: Number, y: Number): AppliedOp = List(op, x, y)
295+
def unapply(app: AppliedOp): Option[(Op, Number, Number)] = {
296+
val app2 = AppliedOpDeco(app)
297+
Some((app2.op, app2.lhs, app2.rhs))
298+
}
299+
}
300+
301+
// === Operations =======================================
302+
303+
type Op = List[Any]
304+
def opClassTag: ClassTag[Op] = new ClassTag[Constant] {
305+
def runtimeClass: Class[_] = classOf[List[_]]
306+
override def unapply(x: Any): Option[List[Any]] = x match {
307+
case op @ (("+" | "*") :: Nil) =>
308+
// Test that it is:
309+
// type Op <: List[Any] // List(id: "+" | "*")
310+
Some(op)
311+
case _ => None
312+
}
313+
}
314+
315+
def OpDeco(t: Op): AbstractOp = new AbstractOp {
316+
def thisOp: Op = t
317+
}
318+
319+
object Op extends OpModule {
320+
object Puls extends PulsExtractor {
321+
def apply(): Op = List("+")
322+
def unapply(x: Op): Boolean = x(0) == "+"
323+
}
324+
object Mult extends MultExtractor {
325+
def apply(): Op = List("*")
326+
def unapply(x: Op): Boolean = x(0) == "*"
327+
}
328+
}
329+
}

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

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

0 commit comments

Comments
 (0)