1
+ object Test extends App {
2
+
3
+ // Explicit ADT
4
+ enum IExp {
5
+ case Lit (i : Int )
6
+ case Neg (e : IExp )
7
+ case Add (l : IExp , r : IExp )
8
+ }
9
+
10
+ val fe : IExp = {
11
+ import IExp ._
12
+ Add (Lit (8 ), Neg (Add (Lit (1 ), Lit (2 ))))
13
+ }
14
+
15
+ // Base algebra
16
+ trait Exp [T ] {
17
+ def lit (i : Int ): T
18
+ def neg (t : T ): T
19
+ def add (l : T , r : T ): T
20
+ }
21
+
22
+ def tf0 [T ] with (e : Exp [T ]): T =
23
+ e.add(e.lit(8 ), e.neg(e.add(e.lit(1 ), e.lit(2 ))))
24
+
25
+ object ExpSyntax {
26
+ def lit [T ](i : Int ) with (e : Exp [T ]): T = e.lit(i)
27
+ def neg [T ](t : T ) with (e : Exp [T ]): T = e.neg(t)
28
+ def add [T ](l : T , r : T ) with (e : Exp [T ]): T = e.add(l, r)
29
+ }
30
+ import ExpSyntax ._ // It's safe to always have these in scope
31
+
32
+ def tf1 [T ] with Exp [T ]: T =
33
+ add(lit(8 ), neg(add(lit(1 ), lit(2 ))))
34
+
35
+ // Base operations as typeclasses
36
+ instance of Exp [Int ] {
37
+ def lit (i : Int ): Int = i
38
+ def neg (t : Int ): Int = - t
39
+ def add (l : Int , r : Int ): Int = l + r
40
+ }
41
+
42
+ instance of Exp [String ] {
43
+ def lit (i : Int ): String = i.toString
44
+ def neg (t : String ): String = s " (- $t) "
45
+ def add (l : String , r : String ): String = s " ( $l + $r) "
46
+ }
47
+
48
+ println(tf1[Int ])
49
+ println(tf1[String ])
50
+
51
+ // Added case in algebra: *
52
+ trait Mult [T ] {
53
+ def mul (l : T , r : T ): T
54
+ }
55
+ object MultSyntax {
56
+ def mul [T ](l : T , r : T ) with (e : Mult [T ]): T = e.mul(l, r)
57
+ }
58
+ import MultSyntax ._
59
+
60
+ def tfm1 [T : Exp : Mult ] = add(lit(7 ), neg(mul(lit(1 ), lit(2 ))))
61
+ def tfm2 [T : Exp : Mult ] = mul(lit(7 ), tf1)
62
+
63
+ instance of Mult [Int ] {
64
+ def mul (l : Int , r : Int ): Int = l * r
65
+ }
66
+
67
+ instance of Mult [String ] {
68
+ def mul (l : String , r : String ): String = s " $l * $r"
69
+ }
70
+
71
+ println(tfm1[Int ])
72
+ println(tfm1[String ])
73
+ println(tfm2[Int ])
74
+ println(tfm2[String ])
75
+
76
+ // Added operation: Deserialization
77
+ enum Tree {
78
+ case Leaf (s : String )
79
+ case Node (s : String , ts : Tree * )
80
+ }
81
+ import Tree ._
82
+
83
+ instance of Exp [Tree ] with Mult [Tree ] {
84
+ def lit (i : Int ): Tree = Node (" Lit" , Leaf (i.toString))
85
+ def neg (t : Tree ): Tree = Node (" Neg" , t)
86
+ def add (l : Tree , r : Tree ): Tree = Node (" Add" , l , r)
87
+ def mul (l : Tree , r : Tree ): Tree = Node (" Mult" , l , r)
88
+ }
89
+
90
+ val tf1Tree = tf1[Tree ]
91
+ val tfm1Tree = tfm1[Tree ]
92
+
93
+ println(s " tf1Tree = $tf1Tree" )
94
+ println(s " tfm1Tree = $tfm1Tree" )
95
+
96
+ class CanThrow private ()
97
+
98
+ object CanThrow {
99
+ private class Exc (msg : String ) extends Exception (msg)
100
+ def _throw (msg : String ) with CanThrow : Nothing = throw new Exc (msg)
101
+ def _try [T ](op : CanThrow |=> T )(handler : String => T ): T = {
102
+ val ct = new CanThrow ()
103
+ try op with ct
104
+ catch {
105
+ case ex : Exception => handler(ex.getMessage)
106
+ }
107
+ }
108
+ }
109
+ import CanThrow ._
110
+
111
+ type Maybe [T ] = CanThrow |=> T
112
+
113
+ def show [T ](op : Maybe [T ]): Unit =
114
+ println(_try(op.toString)(identity))
115
+
116
+ def assertEquals [T ](op1 : Maybe [T ], op2 : Maybe [T ]): Unit =
117
+ _try {
118
+ val x1 = op1
119
+ val x2 = op2
120
+ assert(x1 == x2, " $x1 != $x2" )
121
+ } {
122
+ msg => assert(false , s " thrown: $msg" )
123
+ }
124
+
125
+ def readInt (str : String ): Maybe [Int ] =
126
+ _try(str.toInt)(_ => _throw(s """ Not a number: " $str" """ ))
127
+
128
+ show(readInt(" 2" ))
129
+ show(readInt(" X" ))
130
+
131
+ def fromTree [T ](t : Tree ) with Exp [T ]: Maybe [T ] = t match {
132
+ case Node (" Lit" , Leaf (n)) => lit(readInt(n))
133
+ case Node (" Neg" , t) => neg(fromTree(t))
134
+ case Node (" Add" , l , r) => add(fromTree(l), fromTree(r))
135
+ case _ => _throw(s " Invalid tree $t" )
136
+ }
137
+
138
+ show(fromTree[Int ](tf1Tree))
139
+ show(fromTree[String ](tf1Tree))
140
+ show(fromTree[Tree ](tf1Tree))
141
+
142
+ trait Wrapped {
143
+ def value [T ] with Exp [T ]: T
144
+ }
145
+
146
+ instance of Exp [Wrapped ] {
147
+ def lit (i : Int ) = new Wrapped {
148
+ def value [T ] with (e : Exp [T ]): T = e.lit(i)
149
+ }
150
+ def neg (t : Wrapped ) = new Wrapped {
151
+ def value [T ] with (e : Exp [T ]): T = e.neg(t.value)
152
+ }
153
+ def add (l : Wrapped , r : Wrapped ) = new Wrapped {
154
+ def value [T ] with (e : Exp [T ]): T = e.add(l.value, r.value)
155
+ }
156
+ }
157
+
158
+ show {
159
+ val t = fromTree[Wrapped ](tf1Tree)
160
+ s " ${t.value[Int ]}\n ${t.value[String ]}"
161
+
162
+ }
163
+ def fromTreeExt [T ](recur : => Tree => Maybe [T ]) with Exp [T ]: Tree => Maybe [T ] = {
164
+ case Node (" Lit" , Leaf (n)) => lit(readInt(n))
165
+ case Node (" Neg" , t) => neg(recur(t))
166
+ case Node (" Add" , l , r) => add(recur(l), recur(r))
167
+ case t => _throw(s " Invalid tree $t" )
168
+ }
169
+
170
+ def fix [A ](f : (=> A ) => A ): A = f(fix(f))
171
+
172
+ def fromTree2 [T : Exp ](t : Tree ): Maybe [T ] = fix(fromTreeExt[T ])(t)
173
+
174
+ def fromTreeExt2 [T ](recur : => Tree => Maybe [T ]) with Exp [T ], Mult [T ]: Tree => Maybe [T ] = {
175
+ case Node (" Mult" , l , r) => mul(recur(l), recur(r))
176
+ case t => fromTreeExt(recur)(t)
177
+ }
178
+
179
+ def fromTree3 [T : Exp : Mult ](t : Tree ): Maybe [T ] = fix(fromTreeExt2[T ])(t)
180
+
181
+ assertEquals(fromTree[String ](tf1[Tree ]), tf1[String ])
182
+ assertEquals(fromTree2[String ](tf1[Tree ]), tf1[String ])
183
+ assertEquals(fromTree3[String ](tf1[Tree ]), tf1[String ])
184
+ assertEquals(fromTree3[String ](tfm1[Tree ]), tfm1[String ])
185
+
186
+ enum NCtx { case Pos , Neg }
187
+
188
+ instance NegExp [T ] with (e : Exp [T ]) of Exp [NCtx => T ] {
189
+ import NCtx ._
190
+ def lit (i : Int ) = {
191
+ case Pos => e.lit(i)
192
+ case Neg => e.neg(e.lit(i))
193
+ }
194
+ def neg (x : NCtx => T ): NCtx => T = {
195
+ case Pos => x(Neg )
196
+ case Neg => x(Pos )
197
+ }
198
+ def add (l : NCtx => T , r : NCtx => T ): NCtx => T =
199
+ c => e.add(l(c), r(c))
200
+ }
201
+
202
+ println(tf1[NCtx => String ](NCtx .Pos ))
203
+
204
+ def pushNeg [T ](e : NCtx => T ): T = e(NCtx .Pos )
205
+
206
+ println(pushNeg(tf1[NCtx => String ]))
207
+
208
+ println(pushNeg(pushNeg(pushNeg(tf1))): String )
209
+
210
+ }
0 commit comments