1
+ // A rewrite of Olivier Blanvillain's [adaptation](https://gist.github.com/OlivierBlanvillain/48bb5c66dbb0557da50465809564ee80)
2
+ // of Oleg Kislyov's [lecture notes](http://okmij.org/ftp/tagless-final/course/lecture.pdf)
3
+ // on tagless final interpreters.
4
+ // Main win: Replace Either by an "algebraic effect" using an implicit function type.
1
5
object Test extends App {
2
6
3
7
// Explicit ADT
@@ -12,23 +16,26 @@ object Test extends App {
12
16
Add (Lit (8 ), Neg (Add (Lit (1 ), Lit (2 ))))
13
17
}
14
18
15
- // Base algebra
19
+ // Base trait for type classes
16
20
trait Exp [T ] {
17
21
def lit (i : Int ): T
18
22
def neg (t : T ): T
19
23
def add (l : T , r : T ): T
20
24
}
21
25
26
+ // An example tree
22
27
def tf0 [T ] with (e : Exp [T ]): T =
23
28
e.add(e.lit(8 ), e.neg(e.add(e.lit(1 ), e.lit(2 ))))
24
29
30
+ // Typeclass-style Exp syntax
25
31
object ExpSyntax {
26
32
def lit [T ](i : Int ) with (e : Exp [T ]): T = e.lit(i)
27
33
def neg [T ](t : T ) with (e : Exp [T ]): T = e.neg(t)
28
34
def add [T ](l : T , r : T ) with (e : Exp [T ]): T = e.add(l, r)
29
35
}
30
36
import ExpSyntax ._ // It's safe to always have these in scope
31
37
38
+ // Another tree
32
39
def tf1 [T ] with Exp [T ]: T =
33
40
add(lit(8 ), neg(add(lit(1 ), lit(2 ))))
34
41
@@ -73,7 +80,7 @@ object Test extends App {
73
80
println(tfm2[Int ])
74
81
println(tfm2[String ])
75
82
76
- // Added operation: Deserialization
83
+ // Added operation: serialization
77
84
enum Tree {
78
85
case Leaf (s : String )
79
86
case Node (s : String , ts : Tree * )
@@ -93,6 +100,8 @@ object Test extends App {
93
100
println(s " tf1Tree = $tf1Tree" )
94
101
println(s " tfm1Tree = $tfm1Tree" )
95
102
103
+ // CanThrow infrastructure
104
+ // At some point this will be supported in language and stdlib
96
105
class CanThrow private ()
97
106
98
107
object CanThrow {
@@ -122,6 +131,7 @@ object Test extends App {
122
131
msg => assert(false , s " thrown: $msg" )
123
132
}
124
133
134
+ // Added operation: deserialization
125
135
def readInt (str : String ): Maybe [Int ] =
126
136
_try(str.toInt)(_ => _throw(s """ Not a number: " $str" """ ))
127
137
@@ -183,6 +193,7 @@ object Test extends App {
183
193
assertEquals(fromTree3[String ](tf1[Tree ]), tf1[String ])
184
194
assertEquals(fromTree3[String ](tfm1[Tree ]), tfm1[String ])
185
195
196
+ // Added operation: negation pushdown
186
197
enum NCtx { case Pos , Neg }
187
198
188
199
instance [T ] with (e : Exp [T ]) of Exp [NCtx => T ] {
@@ -232,6 +243,7 @@ object Test extends App {
232
243
case Add (l, r) => e.add(finalize[T ](l), finalize[T ](r))
233
244
}
234
245
246
+ // Abstracting over multiple typeclasses
235
247
type Ring [T ] = Exp [T ] |=> Mult [T ] |=> T
236
248
237
249
def tfm1a [T ]: Ring [T ] = add(lit(7 ), neg(mul(lit(1 ), lit(2 ))))
0 commit comments