|
| 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