Skip to content

Commit abdba3f

Browse files
committed
Add an implementation example of fully abstract interface
* All type are defined as abstract type with subtyping if needed * Abstract constructors and extractors are added to allow pattern matching * Extension methods can be added to the abstract types to simulate methods * Implicit class tags are added to allow correct type tests. If the classes of the implementation are not enough to distinguish the types we can implement cuntom class tags the additionaly check the state of the array. This is showed in the `ListImplementation` where everithing is represented as an `List[Any]`
1 parent ce4633d commit abdba3f

File tree

2 files changed

+359
-0
lines changed

2 files changed

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

0 commit comments

Comments
 (0)