Skip to content

Commit ee09505

Browse files
committed
Fix instance translation
Parameter sections are parameters of classes. New test (that discovered the problem): Olivier's "Revisiting Tagless Final Interpreters" in the new syntax.
1 parent 5b2a5ff commit ee09505

File tree

3 files changed

+230
-2
lines changed

3 files changed

+230
-2
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2572,8 +2572,8 @@ object Parsers {
25722572
}
25732573
else atSpan(start, nameStart) {
25742574
val name = if (isIdent && !isIdent(nme.of)) ident() else EmptyTermName
2575-
val tparams = typeParamClauseOpt(ParamOwner.Def)
2576-
val vparamss = paramClauses(ofInstance = true)
2575+
val tparams = typeParamClauseOpt(ParamOwner.Class)
2576+
val vparamss = paramClauses(ofClass = true, ofInstance = true)
25772577
val parents =
25782578
if (isIdent(nme.of)) {
25792579
in.nextToken()

tests/run/tagless.check

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
5
2+
(8 + (-(1 + 2)))
3+
5
4+
(7 + (-1 * 2))
5+
35
6+
7 * (8 + (-(1 + 2)))
7+
tf1Tree = Node(Add,WrappedArray(Node(Lit,WrappedArray(Leaf(8))), Node(Neg,WrappedArray(Node(Add,WrappedArray(Node(Lit,WrappedArray(Leaf(1))), Node(Lit,WrappedArray(Leaf(2)))))))))
8+
tfm1Tree = Node(Add,WrappedArray(Node(Lit,WrappedArray(Leaf(7))), Node(Neg,WrappedArray(Node(Mult,WrappedArray(Node(Lit,WrappedArray(Leaf(1))), Node(Lit,WrappedArray(Leaf(2)))))))))
9+
2
10+
Not a number: "X"
11+
5
12+
(8 + (-(1 + 2)))
13+
Node(Add,WrappedArray(Node(Lit,WrappedArray(Leaf(8))), Node(Neg,WrappedArray(Node(Add,WrappedArray(Node(Lit,WrappedArray(Leaf(1))), Node(Lit,WrappedArray(Leaf(2)))))))))
14+
5
15+
(8 + (-(1 + 2)))
16+
(8 + ((-1) + (-2)))
17+
(8 + ((-1) + (-2)))
18+
(8 + ((-1) + (-2)))

tests/run/tagless.scala

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
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

Comments
 (0)