Skip to content

Commit f99c7ed

Browse files
committed
Add error handling to quasi free monad
1 parent 01573ad commit f99c7ed

File tree

1 file changed

+152
-110
lines changed

1 file changed

+152
-110
lines changed

modules/quasi/src/main/scala/iota/quasi/quasi/package.scala

Lines changed: 152 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -8,158 +8,181 @@ import TListK.::
88

99
package object quasi {
1010

11-
type Quasi[S[_], A] = quasiImpl.Quasi[S, A]
12-
type Concur[S[_], A] = quasiImpl.Concur[S, A]
13-
type Subseq[S[_], A] = quasiImpl.Subseq[S, A]
11+
type Quasi [S[_], E, A] = quasiImpl.Quasi [S, E, A]
12+
type Concur[S[_], E, A] = quasiImpl.Concur[S, E, A]
13+
type Subseq[S[_], E, A] = quasiImpl.Subseq[S, E, A]
1414

15-
implicit final class QuasiOps[S[_], A](val quasi: Quasi[S, A]) extends AnyVal {
16-
def concur: Concur[S, A] = quasiImpl.toConcur(quasi)
17-
def subseq: Subseq[S, A] = quasiImpl.toSubseq(quasi)
15+
implicit final class QuasiOps[S[_], E, A](val quasi: Quasi[S, E, A]) extends AnyVal {
16+
def concur: Concur[S, E, A] = quasiImpl.toConcur(quasi)
17+
def subseq: Subseq[S, E, A] = quasiImpl.toSubseq(quasi)
1818
}
1919

20-
final implicit class ConcurOps[S[_], A](val concur: Concur[S, A]) extends AnyVal {
21-
def quasi: Quasi[S, A] = quasiImpl.fromConcur(concur)
22-
def subseq: Subseq[S, A] = quasi.subseq
20+
final implicit class ConcurOps[S[_], E, A](val concur: Concur[S, E, A]) extends AnyVal {
21+
def quasi: Quasi[S, E, A] = quasiImpl.fromConcur(concur)
22+
def subseq: Subseq[S, E, A] = quasi.subseq
2323

24-
def ap[B](f: Concur[S, A => B]): Concur[S, B] =
24+
def ap[B](f: Concur[S, E, A => B]): Concur[S, E, B] =
2525
quasiImpl.ap(f.quasi)(concur.quasi).concur
2626

27-
def map[B](f: A => B): Concur[S, B] = ap(Quasi.pure(f).concur)
27+
def map[B](f: A => B): Concur[S, E, B] = ap(Quasi.pure(f).concur)
2828
}
2929

30-
final implicit class SubseqOps[S[_], A](val subseq: Subseq[S, A]) extends AnyVal {
31-
def quasi: Quasi[S, A] = quasiImpl.fromSubseq(subseq)
32-
def concur: Concur[S, A] = quasi.concur
30+
final implicit class SubseqOps[S[_], E, A](val subseq: Subseq[S, E, A]) extends AnyVal {
31+
def quasi: Quasi[S, E, A] = quasiImpl.fromSubseq(subseq)
32+
def concur: Concur[S, E, A] = quasi.concur
3333

34-
def map[B](f: A => B): Subseq[S, B] =
34+
def map[B](f: A => B): Subseq[S, E, B] =
3535
flatMap(a => Quasi.pure(f(a)).subseq)
3636

37-
def flatMap[B](f: A => Subseq[S, B]): Subseq[S, B] =
37+
def flatMap[B](f: A => Subseq[S, E, B]): Subseq[S, E, B] =
3838
quasiImpl.flatMap(subseq.quasi)(f.andThen(_.quasi)).subseq
3939
}
4040

41-
implicit def subseqMonad[S[_]]: Monad[Subseq[S, ?]] = new Monad[Subseq[S, ?]] {
42-
def pure[A](a: A): Subseq[S, A] = Quasi.pure(a).subseq
43-
def flatMap[A, B](fa: Subseq[S, A])(f: A => Subseq[S, B]): Subseq[S, B] =
41+
implicit def subseqMonadError[S[_], E]: MonadError[Subseq[S, E, ?], E] = new MonadError[Subseq[S, E, ?], E] {
42+
def pure[A](a: A): Subseq[S, E, A] = Quasi.pure(a).subseq
43+
def flatMap[A, B](fa: Subseq[S, E, A])(f: A => Subseq[S, E, B]): Subseq[S, E, B] =
4444
fa.flatMap(f)
4545

46-
def tailRecM[A, B](a: A)(f: A => Subseq[S, Either[A, B]]): Subseq[S, B] = ???
46+
def tailRecM[A, B](a: A)(f: A => Subseq[S, E, Either[A, B]]): Subseq[S, E, B] = ???
47+
48+
def handleErrorWith[A](fa: Subseq[S, E, A])(f: E => Subseq[S, E, A]): Subseq[S, E, A] =
49+
quasiImpl.guard(fa.quasi, f.andThen(_.quasi)).subseq
50+
51+
def raiseError[A](e: E): Subseq[S, E, A] =
52+
quasiImpl.raise(e).subseq
4753
}
4854

49-
implicit def concurApplicative[S[_]]: Applicative[Concur[S, ?]] = new Applicative[Concur[S, ?]] {
50-
def pure[A](a: A): Concur[S, A] = Quasi.pure(a).concur
51-
def ap[A, B](ff: Concur[S, A => B])(fa: Concur[S, A]): Concur[S, B] =
55+
implicit def concurApplicative[S[_], E]: Applicative[Concur[S, E, ?]] = new Applicative[Concur[S, E, ?]] {
56+
def pure[A](a: A): Concur[S, E, A] = Quasi.pure(a).concur
57+
def ap[A, B](ff: Concur[S, E, A => B])(fa: Concur[S, E, A]): Concur[S, E, B] =
5258
fa.ap(ff)
5359
}
5460

55-
implicit def subseqConcurParallel[S[_]]: Parallel[Subseq[S, ?], Concur[S, ?]] =
56-
new Parallel[Subseq[S, ?], Concur[S, ?]] {
57-
val parallel: Subseq[S, ?] ~> Concur[S, ?] =
58-
λ[Subseq[S, ?] ~> Concur[S, ?]](_.quasi.concur)
59-
val sequential: Concur[S, ?] ~> Subseq[S, ?] =
60-
λ[Concur[S, ?] ~> Subseq[S, ?]](_.quasi.subseq)
61-
val applicative: Applicative[Concur[S, ?]] = Applicative[Concur[S, ?]]
62-
val monad: Monad[Subseq[S, ?]] = Monad[Subseq[S, ?]]
61+
implicit def subseqConcurParallel[S[_], E]: Parallel[Subseq[S, E, ?], Concur[S, E, ?]] =
62+
new Parallel[Subseq[S, E, ?], Concur[S, E, ?]] {
63+
val parallel: Subseq[S, E, ?] ~> Concur[S, E, ?] =
64+
λ[Subseq[S, E, ?] ~> Concur[S, E, ?]](_.quasi.concur)
65+
val sequential: Concur[S, E, ?] ~> Subseq[S, E, ?] =
66+
λ[Concur[S, E, ?] ~> Subseq[S, E, ?]](_.quasi.subseq)
67+
val applicative: Applicative[Concur[S, E, ?]] = Applicative[Concur[S, E, ?]]
68+
val monad: Monad[Subseq[S, E, ?]] = Monad[Subseq[S, E, ?]]
6369
}
6470

6571
object Quasi {
6672

67-
def pure[S[_], A](a: A): Quasi[S, A] = quasiImpl.pure(a)
68-
def liftF[S[_], A](value: S[A]): Quasi[S, A] = quasiImpl.suspend(value)
69-
70-
def toConcur[S[_]]: Quasi[S, ?] ~> Concur[S, ?] =
71-
λ[Quasi[S, ?] ~> Concur[S, ?]](_.concur)
72-
73-
def toSubseq[S[_]]: Quasi[S, ?] ~> Subseq[S, ?] =
74-
λ[Quasi[S, ?] ~> Subseq[S, ?]](_.subseq)
75-
76-
def foldMap[S[_], M[_], A](quasi: Quasi[S, A])(f: S ~> M)(implicit M: Parallel[M, M]): M[A] =
77-
quasiImpl.evaluator(f,
78-
M.parallel, M.sequential,
79-
M.monad, M.applicative)(quasi)
73+
def pure[S[_], E, A](a: A): Quasi[S, E, A] = quasiImpl.pure(a)
74+
def liftF[S[_], E, A](value: S[A]): Quasi[S, E, A] = quasiImpl.suspend(value)
75+
76+
def toConcur[S[_], E]: Quasi[S, E, ?] ~> Concur[S, E, ?] =
77+
λ[Quasi[S, E, ?] ~> Concur[S, E, ?]](_.concur)
78+
79+
def toSubseq[S[_], E]: Quasi[S, E, ?] ~> Subseq[S, E, ?] =
80+
λ[Quasi[S, E, ?] ~> Subseq[S, E, ?]](_.subseq)
81+
82+
def foldMap[S[_], Zm[_], Za[_], E, A]
83+
(quasi: Quasi[S, E, A])(f: S ~> Zm)(implicit Z: Parallel[Zm, Za], E: MonadError[Zm, E]): Zm[A] =
84+
quasiImpl.evaluator(
85+
f,
86+
Z.parallel,
87+
Z.sequential,
88+
Z.monad,
89+
Z.applicative,
90+
λ[λ[α => (Zm[α], E => Zm[α])] ~> Zm](n => E.handleErrorWith(n._1)(n._2)),
91+
λ[λ[α => E] ~> Zm](E.raiseError(_)))(quasi)
8092
}
8193

8294
private[quasi] sealed trait QuasiImpl {
83-
type Quasi [S[_], A]
84-
type Concur[S[_], A]
85-
type Subseq[S[_], A]
86-
87-
type Effects[S[_]] =
88-
Pure [S, ?] ::
89-
Suspend [S, ?] ::
90-
FlatMap [S, _, ?] ::
91-
Ap [S, _, ?] ::
92-
Raise [S, _, ?] ::
93-
Handle [S, _, ?] ::
95+
type Quasi [S[_], E, A]
96+
type Concur[S[_], E, A]
97+
type Subseq[S[_], E, A]
98+
99+
type Effects[S[_], E] =
100+
Pure [S, ?] ::
101+
Suspend [S, ?] ::
102+
FlatMap [S, E, _, ?] ::
103+
Ap [S, E, _, ?] ::
104+
Guard [S, E, ?] ::
105+
Raise [S, E, ?] ::
94106
TNilK
95107

96108
type Pure[S[_], A] = A
97109
type Suspend[S[_], A] = S[A]
98-
final case class FlatMap[S[_], A, B](fa: Quasi[S, A], f: A => Quasi[S, B])
99-
final case class Ap[S[_], A, B](ff: Quasi[S, A => B], fa: Quasi[S, A])
110+
final case class FlatMap[S[_], E, A, B](fa: Quasi[S, E, A], f: A => Quasi[S, E, B])
111+
final case class Ap[S[_], E, A, B](ff: Quasi[S, E, A => B], fa: Quasi[S, E, A])
112+
final case class Guard[S[_], E, A](fa: Quasi[S, E, A], f: E => Quasi[S, E, A])
100113
type Raise[S[_], E, A] = E
101-
final case class Handle[S[_], E, A](fe: E => Quasi[S, A])
102-
//type Handle[S[_], E, A] = E => Quasi[S, A]
103114

104-
def toRaw[S[_], A](quasi: Quasi[S, A]): CopK[Effects[S], A]
105-
def fromRaw[S[_], A](copK: CopK[Effects[S], A]): Quasi[S, A]
115+
def toRaw[S[_], E, A](quasi: Quasi[S, E, A]): CopK[Effects[S, E], A]
116+
def fromRaw[S[_], E, A](copK: CopK[Effects[S, E], A]): Quasi[S, E, A]
106117

107-
def toConcur[S[_], A](quasi: Quasi[S, A]): Concur[S, A]
108-
def fromConcur[S[_], A](subseq: Concur[S, A]): Quasi[S, A]
118+
def toConcur[S[_], E, A](quasi: Quasi[S, E, A]): Concur[S, E, A]
119+
def fromConcur[S[_], E, A](subseq: Concur[S, E, A]): Quasi[S, E, A]
109120

110-
def toSubseq[S[_], A](quasi: Quasi[S, A]): Subseq[S, A]
111-
def fromSubseq[S[_], A](subseq: Subseq[S, A]): Quasi[S, A]
121+
def toSubseq[S[_], E, A](quasi: Quasi[S, E, A]): Subseq[S, E, A]
122+
def fromSubseq[S[_], E, A](subseq: Subseq[S, E, A]): Quasi[S, E, A]
112123

113-
def pure[S[_], A](a: A): Quasi[S, A]
114-
def suspend[S[_], A](value: S[A]): Quasi[S, A]
115-
def flatMap[S[_], A, B](fa: Quasi[S, A])(f: A => Quasi[S, B]): Quasi[S, B]
116-
def ap[S[_], A, B](ff: Quasi[S, A => B])(fa: Quasi[S, A]): Quasi[S, B]
124+
def pure [S[_], E, A](a: A): Quasi[S, E, A]
125+
def suspend[S[_], E, A](value: S[A]): Quasi[S, E, A]
126+
def flatMap[S[_], E, A, B](fa: Quasi[S, E, A])(f: A => Quasi[S, E, B]): Quasi[S, E, B]
127+
def ap [S[_], E, A, B](ff: Quasi[S, E, A => B])(fa: Quasi[S, E, A]): Quasi[S, E, B]
128+
def guard [S[_], E, A](fa: Quasi[S, E, A], f: E => Quasi[S, E, A]): Quasi[S, E, A]
129+
def raise [S[_], E, A](e: E): Quasi[S, E, A]
117130

118-
type Evaluator[S[_], M[_]] = Quasi[S, ?] ~> M
131+
type Evaluator[S[_], M[_], E] = Quasi[S, E, ?] ~> M
119132

120-
def evaluator[S[_], Zm[_], Za[_]](
133+
def evaluator[S[_], Zm[_], Za[_], E](
121134
f : S ~> Zm,
122135
parallel : Zm ~> Za,
123136
sequential: Za ~> Zm,
124137
Zm : Monad[Zm],
125-
Za : Applicative[Za]
126-
): Evaluator[S, Zm]
138+
Za : Applicative[Za],
139+
guard : λ[α => (Zm[α], E => Zm[α])] ~> Zm,
140+
raise : λ[α => E] ~> Zm
141+
): Evaluator[S, Zm, E]
127142
}
128143

129144
private[quasi] val quasiImpl: QuasiImpl = new QuasiImpl {
130-
type Quasi [S[_], A] = CopK[Effects[S], A]
131-
type Concur[S[_], A] = CopK[Effects[S], A]
132-
type Subseq[S[_], A] = CopK[Effects[S], A]
145+
type Quasi [S[_], E, A] = CopK[Effects[S, E], A]
146+
type Concur[S[_], E, A] = CopK[Effects[S, E], A]
147+
type Subseq[S[_], E, A] = CopK[Effects[S, E], A]
133148

134-
def toRaw[S[_], A](quasi: Quasi[S, A]): CopK[Effects[S], A] = quasi
135-
def fromRaw[S[_], A](copK: CopK[Effects[S], A]): Quasi[S, A] = copK
149+
def toRaw[S[_], E, A](quasi: Quasi[S, E, A]): CopK[Effects[S, E], A] = quasi
150+
def fromRaw[S[_], E, A](copK: CopK[Effects[S, E], A]): Quasi[S, E, A] = copK
136151

137-
def toConcur[S[_], A](quasi: Quasi[S, A]): Concur[S, A] = quasi
138-
def fromConcur[S[_], A](subseq: Concur[S, A]): Quasi[S, A] = subseq
152+
def toConcur[S[_], E, A](quasi: Quasi[S, E, A]): Concur[S, E, A] = quasi
153+
def fromConcur[S[_], E, A](subseq: Concur[S, E, A]): Quasi[S, E, A] = subseq
139154

140-
def toSubseq[S[_], A](quasi: Quasi[S, A]): Subseq[S, A] = quasi
141-
def fromSubseq[S[_], A](subseq: Subseq[S, A]): Quasi[S, A] = subseq
155+
def toSubseq[S[_], E, A](quasi: Quasi[S, E, A]): Subseq[S, E, A] = quasi
156+
def fromSubseq[S[_], E, A](subseq: Subseq[S, E, A]): Quasi[S, E, A] = subseq
142157

143-
def pure[S[_], A](a: A): Quasi[S, A] =
144-
CopK.unsafeApply[Effects[S], Pure[S, ?], A](0, a)
158+
def pure[S[_], E, A](a: A): Quasi[S, E, A] =
159+
CopK.unsafeApply[Effects[S, E], Pure[S, ?], A](0, a)
145160

146-
def suspend[S[_], A](value: S[A]): Quasi[S, A] =
147-
CopK.unsafeApply[Effects[S], Suspend[S, ?], A](1, value)
161+
def suspend[S[_], E, A](value: S[A]): Quasi[S, E, A] =
162+
CopK.unsafeApply[Effects[S, E], Suspend[S, ?], A](1, value)
148163

149-
def flatMap[S[_], A, B](fa: Quasi[S, A])(f: A => Quasi[S, B]): Quasi[S, B] =
150-
CopK.unsafeApply[Effects[S], FlatMap[S, A, ?], B](2, FlatMap[S, A, B](fa, f))
164+
def flatMap[S[_], E, A, B](fa: Quasi[S, E, A])(f: A => Quasi[S, E, B]): Quasi[S, E, B] =
165+
CopK.unsafeApply[Effects[S, E], FlatMap[S, E, A, ?], B](2, FlatMap[S, E, A, B](fa, f))
151166

152-
def ap[S[_], A, B](ff: Quasi[S, A => B])(fa: Quasi[S, A]): Quasi[S, B] =
153-
CopK.unsafeApply[Effects[S], Ap[S, A, ?], B](3, Ap[S, A, B](ff, fa))
167+
def ap[S[_], E, A, B](ff: Quasi[S, E, A => B])(fa: Quasi[S, E, A]): Quasi[S, E, B] =
168+
CopK.unsafeApply[Effects[S, E], Ap[S, E, A, ?], B](3, Ap[S, E, A, B](ff, fa))
154169

155-
def evaluator[S[_], Zm[_], Za[_]](
170+
def guard[S[_], E, A](fa: Quasi[S, E, A], f: E => Quasi[S, E, A]): Quasi[S, E, A] =
171+
CopK.unsafeApply[Effects[S, E], Guard[S, E, ?], A](4, Guard[S, E, A](fa, f))
172+
173+
def raise[S[_], E, A](e: E): Quasi[S, E, A] =
174+
CopK.unsafeApply[Effects[S, E], Raise[S, E, ?], A](5, e)
175+
176+
def evaluator[S[_], Zm[_], Za[_], E](
156177
f : S ~> Zm,
157178
parallel : Zm ~> Za,
158179
sequential: Za ~> Zm,
159180
Zm : Monad[Zm],
160-
Za : Applicative[Za]
161-
): Evaluator[S, Zm] = new Evaluator[S, Zm] {
162-
def apply[A](quasi: Quasi[S, A]): Zm[A] =
181+
Za : Applicative[Za],
182+
guard : λ[α => (Zm[α], E => Zm[α])] ~> Zm,
183+
raise : λ[α => E] ~> Zm
184+
): Evaluator[S, Zm, E] = new Evaluator[S, Zm, E] {
185+
def apply[A](quasi: Quasi[S, E, A]): Zm[A] =
163186
Zm.tailRecM(quasi)(q => (q.index: @scala.annotation.switch) match {
164187
case 0 =>
165188
val a: A = q.value.asInstanceOf[A]
@@ -168,16 +191,23 @@ package object quasi {
168191
val sa: S[A] = q.value.asInstanceOf[S[A]]
169192
Zm.map(f(sa))(Right(_))
170193
case 2 =>
171-
val n: FlatMap[S, Any, A] = q.value.asInstanceOf[FlatMap[S, Any, A]]
194+
val n: FlatMap[S, E, Any, A] = q.value.asInstanceOf[FlatMap[S, E, Any, A]]
172195
Zm.map(this(n.fa))(z => Left(n.f(z)))
173196
case 3 =>
174-
val n: Ap[S, Any, A] = q.value.asInstanceOf[Ap[S, Any, A]]
197+
val n: Ap[S, E, Any, A] = q.value.asInstanceOf[Ap[S, E, Any, A]]
175198
Zm.map(
176199
sequential(Za.ap(
177200
parallel(this(n.ff)))(
178201
parallel(this(n.fa))))
179202
)(Right(_))
180-
case _ => scala.Predef.???
203+
case 4 =>
204+
val n: Guard[S, E, A] = q.value.asInstanceOf[Guard[S, E, A]]
205+
Zm.map(guard((this(n.fa), n.f.andThen(this(_)))))(Right(_))
206+
case 5 =>
207+
val e: E = q.value.asInstanceOf[E]
208+
Zm.map(raise(e))(Right(_))
209+
case _ =>
210+
sys.error("unreachable internal state")
181211
})
182212
}
183213

@@ -197,35 +227,43 @@ package quasi {
197227
trait MathOp[A]
198228
case class ConstInt(value: Int) extends MathOp[Int]
199229
case class Add(x: Int, y: Int) extends MathOp[Int]
230+
case class Div(x: Int, y: Int) extends MathOp[Int]
200231
case class Neg(x: Int) extends MathOp[Int]
201232

202233
trait Math[F[_]] { underlying =>
203234
def const(value: Int): F[Int]
204235
def add(x: Int, y: Int): F[Int]
236+
def div(x: Int, y: Int): F[Int]
205237
def neg(x: Int): F[Int]
206238

207239
final def mapK[G[_]](f: F ~> G): Math[G] = new Math[G] {
208240
def const(value: Int): G[Int] = f(underlying.const(value))
209241
def add(x: Int, y: Int): G[Int] = f(underlying.add(x, y))
242+
def div(x: Int, y: Int): G[Int] = f(underlying.div(x, y))
210243
def neg(x: Int): G[Int] = f(underlying.neg(x))
211244
}
212245
}
213246

214247
object Math {
215-
def quasi: Math[Quasi[MathOp, ?]] = new Math[Quasi[MathOp, ?]] {
216-
def const(value: Int): Quasi[MathOp, Int] = Quasi.liftF(ConstInt(value))
217-
def add(x: Int, y: Int): Quasi[MathOp, Int] = Quasi.liftF(Add(x, y))
218-
def neg(x: Int): Quasi[MathOp, Int] = Quasi.liftF(Neg(x))
248+
def quasi: Math[Quasi[MathOp, Throwable, ?]] = new Math[Quasi[MathOp, Throwable, ?]] {
249+
def const(value: Int): Quasi[MathOp, Throwable, Int] = Quasi.liftF(ConstInt(value))
250+
def add(x: Int, y: Int): Quasi[MathOp, Throwable, Int] = Quasi.liftF(Add(x, y))
251+
def div(x: Int, y: Int): Quasi[MathOp, Throwable, Int] = Quasi.liftF(Div(x, y))
252+
def neg(x: Int): Quasi[MathOp, Throwable, Int] = Quasi.liftF(Neg(x))
219253
}
220254

221-
def concur: Math[Concur[MathOp, ?]] = quasi.mapK[Concur[MathOp, ?]](Quasi.toConcur)
222-
def subseq: Math[Subseq[MathOp, ?]] = quasi.mapK[Subseq[MathOp, ?]](Quasi.toSubseq)
255+
def concur: Math[Concur[MathOp, Throwable, ?]] = quasi.mapK[Concur[MathOp, Throwable, ?]](Quasi.toConcur)
256+
def subseq: Math[Subseq[MathOp, Throwable, ?]] = quasi.mapK[Subseq[MathOp, Throwable, ?]](Quasi.toSubseq)
223257
}
224258

225-
val interp: MathOp ~> Id = λ[MathOp ~> Id] {
226-
case ConstInt(value) => value
227-
case Add(x, y) => x + y
228-
case Neg(x) => -x
259+
import scala.util.Try
260+
implicit val parallelTry: Parallel[Try, Try] = Parallel.identity
261+
262+
val interp: MathOp ~> Try = λ[MathOp ~> Try] {
263+
case ConstInt(value) => Try(value)
264+
case Add(x, y) => Try(x + y)
265+
case Div(x, y) => Try(x / y)
266+
case Neg(x) => Try(-x)
229267
}
230268

231269
val math = Math.subseq
@@ -244,11 +282,15 @@ package quasi {
244282
val program2 = for {
245283
foo <- math.const(0)
246284
bar <- List(program0, program1).parSequence
247-
} yield bar.foldLeft(foo)(_ + _)
285+
} yield bar.foldLeft(foo)(_ / _)
286+
287+
val program3 = program2.handleErrorWith(_ => math.const(-100))
248288

249-
scala.Predef.println(program2)
289+
scala.Predef.println("program:")
290+
scala.Predef.println(program3)
250291

251-
val res = Quasi.foldMap(program2.quasi)(interp)
292+
val res = Quasi.foldMap(program3.quasi)(interp)
293+
scala.Predef.println("res:")
252294
scala.Predef.println(res)
253295

254296
}

0 commit comments

Comments
 (0)