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+ }
0 commit comments