Skip to content

Commit 01573ad

Browse files
committed
Add hunchbacked Quasimonad; both friendly and a monster
1 parent 058ee26 commit 01573ad

File tree

2 files changed

+282
-2
lines changed

2 files changed

+282
-2
lines changed

build.sbt

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
lazy val root = (project in file("."))
22
.settings(noPublishSettings)
33
.aggregate(coreJVM, coreJS)
4+
.aggregate(quasiJVM, quasiJS)
45
.aggregate(testsJVM, testsJS)
56
.aggregate(examplesCatsJVM, examplesCatsJS)
67
.aggregate(examplesScalazJVM, examplesScalazJS)
78
.aggregate(bench)
89
.aggregate(corezJVM, corezJS)
10+
.aggregate(quasizJVM, quasizJS)
911
.aggregate(testszJVM, testszJS)
1012
.aggregate(readme, docs)
1113

@@ -15,8 +17,8 @@ lazy val core = module("core", hideFolder = true)
1517
flags = "cats" :: Nil,
1618
yaxScala = true))
1719
.crossDepSettings(
18-
%%("cats-core"),
19-
%%("cats-free"))
20+
"org.typelevel" %% "cats-core" % "1.0.0-RC1",
21+
"org.typelevel" %% "cats-free" % "1.0.0-RC1")
2022

2123
lazy val coreJVM = core.jvm
2224
lazy val coreJS = core.js
@@ -32,6 +34,26 @@ lazy val corez = module("core", hideFolder = true, prefixSuffix = "z")
3234
lazy val corezJVM = corez.jvm
3335
lazy val corezJS = corez.js
3436

37+
lazy val quasi = module("quasi", hideFolder = true)
38+
.dependsOn(core)
39+
.settings(macroSettings)
40+
.settings(yax(file("modules/quasi/src/main/scala"), Compile,
41+
flags = "cats" :: Nil,
42+
yaxScala = true))
43+
44+
lazy val quasiJVM = quasi.jvm
45+
lazy val quasiJS = quasi.js
46+
47+
lazy val quasiz = module("quasi", hideFolder = true, prefixSuffix = "z")
48+
.dependsOn(corez)
49+
.settings(macroSettings)
50+
.settings(yax(file("modules/quasi/src/main/scala"), Compile,
51+
flags = "scalaz" :: Nil,
52+
yaxScala = true))
53+
54+
lazy val quasizJVM = quasiz.jvm
55+
lazy val quasizJS = quasiz.js
56+
3557
lazy val tests = module("tests", hideFolder = true)
3658
.dependsOn(core)
3759
.settings(noPublishSettings)
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
package iota //#=cats
2+
package iotaz //#=scalaz
3+
4+
import cats._ //#=cats
5+
import scalaz._ //#=scalaz
6+
7+
import TListK.::
8+
9+
package object quasi {
10+
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]
14+
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)
18+
}
19+
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
23+
24+
def ap[B](f: Concur[S, A => B]): Concur[S, B] =
25+
quasiImpl.ap(f.quasi)(concur.quasi).concur
26+
27+
def map[B](f: A => B): Concur[S, B] = ap(Quasi.pure(f).concur)
28+
}
29+
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
33+
34+
def map[B](f: A => B): Subseq[S, B] =
35+
flatMap(a => Quasi.pure(f(a)).subseq)
36+
37+
def flatMap[B](f: A => Subseq[S, B]): Subseq[S, B] =
38+
quasiImpl.flatMap(subseq.quasi)(f.andThen(_.quasi)).subseq
39+
}
40+
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] =
44+
fa.flatMap(f)
45+
46+
def tailRecM[A, B](a: A)(f: A => Subseq[S, Either[A, B]]): Subseq[S, B] = ???
47+
}
48+
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] =
52+
fa.ap(ff)
53+
}
54+
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, ?]]
63+
}
64+
65+
object Quasi {
66+
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)
80+
}
81+
82+
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, _, ?] ::
94+
TNilK
95+
96+
type Pure[S[_], A] = A
97+
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])
100+
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]
103+
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]
106+
107+
def toConcur[S[_], A](quasi: Quasi[S, A]): Concur[S, A]
108+
def fromConcur[S[_], A](subseq: Concur[S, A]): Quasi[S, A]
109+
110+
def toSubseq[S[_], A](quasi: Quasi[S, A]): Subseq[S, A]
111+
def fromSubseq[S[_], A](subseq: Subseq[S, A]): Quasi[S, A]
112+
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]
117+
118+
type Evaluator[S[_], M[_]] = Quasi[S, ?] ~> M
119+
120+
def evaluator[S[_], Zm[_], Za[_]](
121+
f : S ~> Zm,
122+
parallel : Zm ~> Za,
123+
sequential: Za ~> Zm,
124+
Zm : Monad[Zm],
125+
Za : Applicative[Za]
126+
): Evaluator[S, Zm]
127+
}
128+
129+
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]
133+
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
136+
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
139+
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
142+
143+
def pure[S[_], A](a: A): Quasi[S, A] =
144+
CopK.unsafeApply[Effects[S], Pure[S, ?], A](0, a)
145+
146+
def suspend[S[_], A](value: S[A]): Quasi[S, A] =
147+
CopK.unsafeApply[Effects[S], Suspend[S, ?], A](1, value)
148+
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))
151+
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))
154+
155+
def evaluator[S[_], Zm[_], Za[_]](
156+
f : S ~> Zm,
157+
parallel : Zm ~> Za,
158+
sequential: Za ~> Zm,
159+
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] =
163+
Zm.tailRecM(quasi)(q => (q.index: @scala.annotation.switch) match {
164+
case 0 =>
165+
val a: A = q.value.asInstanceOf[A]
166+
Zm.pure(Right(a))
167+
case 1 =>
168+
val sa: S[A] = q.value.asInstanceOf[S[A]]
169+
Zm.map(f(sa))(Right(_))
170+
case 2 =>
171+
val n: FlatMap[S, Any, A] = q.value.asInstanceOf[FlatMap[S, Any, A]]
172+
Zm.map(this(n.fa))(z => Left(n.f(z)))
173+
case 3 =>
174+
val n: Ap[S, Any, A] = q.value.asInstanceOf[Ap[S, Any, A]]
175+
Zm.map(
176+
sequential(Za.ap(
177+
parallel(this(n.ff)))(
178+
parallel(this(n.fa))))
179+
)(Right(_))
180+
case _ => scala.Predef.???
181+
})
182+
}
183+
184+
}
185+
186+
}
187+
188+
// example
189+
//#+cats
190+
import cats.implicits._
191+
package quasi {
192+
193+
object Example {
194+
195+
def main(args: Array[String]): Unit = {
196+
197+
trait MathOp[A]
198+
case class ConstInt(value: Int) extends MathOp[Int]
199+
case class Add(x: Int, y: Int) extends MathOp[Int]
200+
case class Neg(x: Int) extends MathOp[Int]
201+
202+
trait Math[F[_]] { underlying =>
203+
def const(value: Int): F[Int]
204+
def add(x: Int, y: Int): F[Int]
205+
def neg(x: Int): F[Int]
206+
207+
final def mapK[G[_]](f: F ~> G): Math[G] = new Math[G] {
208+
def const(value: Int): G[Int] = f(underlying.const(value))
209+
def add(x: Int, y: Int): G[Int] = f(underlying.add(x, y))
210+
def neg(x: Int): G[Int] = f(underlying.neg(x))
211+
}
212+
}
213+
214+
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))
219+
}
220+
221+
def concur: Math[Concur[MathOp, ?]] = quasi.mapK[Concur[MathOp, ?]](Quasi.toConcur)
222+
def subseq: Math[Subseq[MathOp, ?]] = quasi.mapK[Subseq[MathOp, ?]](Quasi.toSubseq)
223+
}
224+
225+
val interp: MathOp ~> Id = λ[MathOp ~> Id] {
226+
case ConstInt(value) => value
227+
case Add(x, y) => x + y
228+
case Neg(x) => -x
229+
}
230+
231+
val math = Math.subseq
232+
233+
val program0 = for {
234+
x <- math.const(1)
235+
y <- math.const(2)
236+
z <- math.add(x, y)
237+
} yield z + 10
238+
239+
val program1 = for {
240+
a <- math.const(100)
241+
b <- math.neg(a)
242+
} yield a + b
243+
244+
val program2 = for {
245+
foo <- math.const(0)
246+
bar <- List(program0, program1).parSequence
247+
} yield bar.foldLeft(foo)(_ + _)
248+
249+
scala.Predef.println(program2)
250+
251+
val res = Quasi.foldMap(program2.quasi)(interp)
252+
scala.Predef.println(res)
253+
254+
}
255+
256+
}
257+
}
258+
//#-cats

0 commit comments

Comments
 (0)