diff --git a/.gitignore b/.gitignore
index 6da9171..8766f35 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
-
target/
lib_managed/
src_managed/
project/boot/
.history
.cache
+.idea
diff --git a/README.md b/README.md
index f743001..64d98bf 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,20 @@
> All versions are published to bintray https://bintray.com/projectseptemberinc/
+
+### v0.6.5:
+
+- first scala 2.12 support
+- cross-compiling 2.11.8 & 2.12
+- depends on cats 0.8.0
+
+
+### v0.6.1/2:
+
+- added `Freekit`/`Freekito` helpers to reduce `.freek` boilerplate in basic cases
+- added `transpile` to flatten Free programs combining Free programs
+- added `:&&:` operator to combine group of interpreters
+
### v0.6.0:
@@ -77,14 +91,14 @@ Freek is just a few helpers & tricks to make it straightforward to manipulate Fr
```
# in build.sbt
-scalaVersion := "2.11.8"
+scalaVersion := "2.12.0" // (or 2.11.8)
resolvers += Resolver.bintrayRepo("projectseptemberinc", "maven")
+scalacOptions := Seq("-Ypartial-unification") //if running 2.12
+
libraryDependencies ++= Seq(
- "com.projectseptember" %% "freek" % "0.6.0" // with cats-0.7.0
-// "com.projectseptember" %% "freek" % "0.6.0_cats-0.4.1" // with cats-0.4.1
-// "com.projectseptember" %% "freek" % "0.6.0_cats-0.6.1" // with cats-0.6.1
+ "com.projectseptember" %% "freek" % "0.6.5"
, "org.spire-math" %% "kind-projector" % "0.7.1"
, "com.milessabin" %% "si2712fix-plugin" % "1.2.0"
)
@@ -318,7 +332,7 @@ To prepend one or more DSL to an existing combination of DSL into a new program,
for {
_ <- Log.debug(s"Searching for value id: $id").freek[PRG]
name <- KVS.Get(id).freek[PRG]
- e <- DB.findById(id).freek[PRG]
+ e <- DB.findById(id).expand[PRG]
file <- File.Get(e.file).freek[PRG]
_ <- Log.debug(s"Found file:$file").freek[PRG]
} yield (file)
@@ -329,7 +343,7 @@ Please note:
- there is no `NilDSL` at the end because it's brought by `DBService.PRG`
- `:|:` also appends a list of DSL at the end
-
+- the use of the `expand` function instead of `freek` for the findById operation, the `expand` function allows the use of a program defined with a smaller DSL in one with a bigger DSL
@@ -617,6 +631,17 @@ To make the difference between `:|:` and `:||:`, please remind the following:
- `:||:` is like operator `++` for Scala `Seq`, it appends 2 (coproduct) sequences of DSL.
+
+#### Combine group of interpreters with `:&&:`
+
+```scala
+val fooInterpreters = barInterpreter :&: logInterpreter :|: repoInterpreter
+val barInterpreters = fooInterpreter :&: logInterpreter :|: repoInterpreter
+
+val interpreters = fooInterpreters :&&: barInterpreters
+```
+
+- `:&&:` is like operator `++` for Scala `Seq`, it appends 2 sequences of interpreters.
#### Unstack results with `.peelRight` / `.peelRight2` / `.peelRight3`
@@ -660,6 +685,57 @@ Instead of `Foo2(i).freek[PRG].onionT[O].peelRight2`, you can write `Foo2(i).fre
Instead of `Foo2(i).freek[PRG].onionT[O].peelRight3`, you can write `Foo2(i).freek[PRG].onionT3[O]`
+
+#### Bored adding `.free[PRG]` on each line? Use `Freekit` trick
+
+```scala
+type PRG = Foo1 :|: Foo2 :|: Log :|: NilDSL
+val PRG = DSL.Make[PRG]
+
+// remark that you pass the value PRG here
+object M extends Freekit(PRG) {
+ val prg = for {
+ aOpt <- Foo1.Bar1(7)
+ _ <- Log.Info(s"aOpt:$aOpt")
+ a <- aOpt match {
+ case Some(a) => for {
+ a <- Foo2.Bar21(a)
+ _ <- Log.Info(s"a1:$a")
+ } yield (a)
+ case None => for {
+ a <- Foo2.Bar22
+ _ <- Log.Info(s"a2:$a")
+ } yield (a)
+ }
+ } yield (a)
+}
+```
+
+This works in basic cases & naturally as soon as you have embedded `for-comprehension`, scalac inference makes it less efficient.
+
+
+#### Bored adding `.free[PRG].onionT[O]` on each line? Use `Freekito` trick
+
+```scala
+type PRG = Foo1 :|: Foo2 :|: Log :|: NilDSL
+val PRG = DSL.Make[PRG]
+
+// remark that you pass the value PRG here
+object MO extends Freekito(PRG) {
+ // you need to create this type O to reify the Onion
+ type O = Option :&: Bulb
+
+ val prg = for {
+ a <- Foo1.Bar1(7)
+ _ <- Log.Info(s"a:$a")
+ a <- Foo2.Bar21(a)
+ } yield (a)
+}
+```
+
+This works in basic cases & naturally as soon as you have embedded `for-comprehension`, scalac inference makes it less efficient.
+
+
## Reminding motivations
diff --git a/build.sbt b/build.sbt
index c6f7e85..b8681e1 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,32 +1,40 @@
lazy val commonSettings = Seq(
organization := "com.projectseptember"
- , version := "0.6.0"
+ , version := "0.6.7"
, resolvers ++= Seq(
Resolver.mavenLocal
, Resolver.sonatypeRepo("releases")
- , Resolver.sonatypeRepo("snapshots")
- )
+ , Resolver.sonatypeRepo("snapshots"))
, scalaVersion := "2.11.8"
+ , crossScalaVersions := Seq("2.11.8", "2.12.0", "2.12.1")
, bintrayOrganization := Some("projectseptemberinc")
, licenses += ("Apache-2.0", url("http://www.apache.org/licenses/LICENSE-2.0"))
- , addCompilerPlugin("com.milessabin" % "si2712fix-plugin" % "1.2.0" cross CrossVersion.full)
- , addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.7.1")
- , libraryDependencies ++= Seq(
- "org.typelevel" %% "cats" % "0.7.0"
- , "com.milessabin" % "si2712fix-library" % "1.2.0" cross CrossVersion.full
- , "org.scalatest" % "scalatest_2.11" % "3.0.0" % "test"
- , "org.typelevel" %% "discipline" % "0.4" % "test"
- , "org.typelevel" %% "cats-laws" % "0.6.1"
- )
-
+ , addCompilerPlugin("org.spire-math" % "kind-projector" % "0.9.3" cross CrossVersion.binary)
)
-lazy val root = (project in file(".")).
- settings(commonSettings: _*).
- settings(
+def scalacOptionsVersion(scalaVersion: String) = {
+ Seq(
+ "-feature"
+ , "-language:higherKinds"
+ ) ++ (CrossVersion.partialVersion(scalaVersion) match {
+ case Some((2, scalaMajor)) if scalaMajor == 12 => Seq("-Ypartial-unification")
+ case _ => Nil
+ })
+}
+
+lazy val root = (project in file("."))
+ .settings(commonSettings: _*)
+ .settings(
name := "freek",
- scalacOptions ++= Seq(
- "-feature"
- , "-language:higherKinds"
- )
+ scalacOptions ++= scalacOptionsVersion(scalaVersion.value)
+ )
+ .settings(
+ libraryDependencies ++= Seq(
+ "org.typelevel" %% "cats-free" % "0.9.0"
+ , "org.scalatest" %% "scalatest" % "3.0.0" % Test
+ ) ++ (CrossVersion.partialVersion(scalaVersion.value) match {
+ case Some((2, scalaMajor)) if scalaMajor == 11 =>
+ compilerPlugin("com.milessabin" % "si2712fix-plugin" % "1.2.0" cross CrossVersion.full) :: Nil
+ case _ => Nil
+ })
)
diff --git a/project/build.properties b/project/build.properties
index a6e117b..27e88aa 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version=0.13.8
+sbt.version=0.13.13
diff --git a/src/main/scala/CopK.scala b/src/main/scala/CopK.scala
index b1786b9..1950cfe 100644
--- a/src/main/scala/CopK.scala
+++ b/src/main/scala/CopK.scala
@@ -1,7 +1,6 @@
package freek
import cats.~>
-import cats.data.Xor
/** Higher-Kinded Coproduct (exactly like shapeless Coproduct but higher-kinded)
@@ -372,6 +371,128 @@ trait PrependHKLower {
}
+}
+
+
+trait AppendHK[L[_] <: CopK[_], H[_]] {
+ type Out[_] <: CopK[_]
+
+ def apply[A](ha: L[A]): Out[A]
+ def single[A](ha: H[A]): Out[A]
+
+ def nat[R[_], A](out: Out[A], nat2: L ~> R, nat1: H ~> R): R[A]
+}
+
+object AppendHK extends AppendHKLower {
+
+ def apply[L[_] <: CopK[_], H[_]]
+ (implicit prep: AppendHK[L, H]): Aux[L, H, prep.Out] = prep
+
+ type Aux[L[_] <: CopK[_], H[_], Out0[_] <: CopK[_]] = AppendHK[L, H] { type Out[t] = Out0[t] }
+
+ implicit def in1[H1[_], H2[_]]: Aux[In1[H1, ?], H2, In2[H1, H2, ?]] =
+ new AppendHK[In1[H1, ?], H2] {
+ type Out[t] = In2[H1, H2, t]
+
+ def apply[A](c: In1[H1, A]): Out[A] = In2l(c.head)
+ def single[A](ha: H2[A]): Out[A] = In2r(ha)
+
+ def nat[R[_], A](out: In2[H1, H2, A], nat1: In1[H1, ?] ~> R, nat2: H2 ~> R): R[A] = out match {
+ case In2l(l) => nat1(In1(l))
+ case In2r(r) => nat2(r)
+ }
+ }
+
+ implicit def in2[H1[_], H2[_], H3[_]]: Aux[In2[H1, H2, ?], H3, In3[H1, H2, H3, ?]] =
+ new AppendHK[In2[H1, H2, ?], H3] {
+ type Out[t] = In3[H1, H2, H3, t]
+
+ def apply[A](c: In2[H1, H2, A]): Out[A] = c match {
+ case In2l(left) => In3l(left)
+ case In2r(right) => In3m(right)
+ }
+
+ def single[A](ha: H3[A]): Out[A] = In3r(ha)
+
+ def nat[R[_], A](out: In3[H1, H2, H3, A], nat1: In2[H1, H2, ?] ~> R, nat2: H3 ~> R): R[A] = out match {
+ case In3l(l) => nat1(In2l(l))
+ case In3m(m) => nat1(In2r(m))
+ case In3r(r) => nat2(r)
+ }
+ }
+
+ implicit def in3[H1[_], H2[_], H3[_], H4[_]]: Aux[In3[H1, H2, H3, ?], H4, AppendK[In3[H1, H2, H3, ?], In1[H4, ?], ?]] =
+ new AppendHK[In3[H1, H2, H3, ?], H4] {
+ type Out[t] = AppendK[In3[H1, H2, H3, ?], In1[H4, ?], t]
+
+ def apply[A](c: In3[H1, H2, H3, A]): Out[A] = Aplk(c)
+
+ def single[A](ha: H4[A]): Out[A] = Aprk(In1(ha))
+
+ def nat[R[_], A](out: AppendK[In3[H1, H2, H3, ?], In1[H4, ?], A], nat1: In3[H1, H2, H3, ?] ~> R, nat2: H4 ~> R): R[A] = out match {
+ case Aplk(m) => nat1(m)
+ case Aprk(In1(l)) => nat2(l)
+ }
+ }
+
+ implicit def append1[H1[_], H2[_], L[_] <: CopK[_]]: Aux[AppendK[L, In1[H1, ?], ?], H2, AppendK[L, In2[H1, H2, ?], ?]] =
+ new AppendHK[AppendK[L, In1[H1, ?], ?], H2] {
+ type Out[t] = AppendK[L, In2[H1, H2, ?], t]
+
+ def apply[A](c: AppendK[L, In1[H1, ?], A]): Out[A] = c match {
+ case Aplk(l) => Aplk(l)
+ case Aprk(In1(r)) => Aprk(In2l(r))
+ }
+
+ def single[A](ha: H2[A]): Out[A] = Aprk(In2r(ha))
+
+ def nat[RR[_], A](out: AppendK[L, In2[H1, H2, ?], A], nat1: AppendK[L, In1[H1, ?], ?] ~> RR, nat2: H2 ~> RR): RR[A] = out match {
+ case Aplk(r) => nat1(Aplk(r))
+ case Aprk(In2l(h1)) => nat1(Aprk(In1(h1)))
+ case Aprk(In2r(h2)) => nat2(h2)
+ }
+ }
+
+ implicit def append2[H1[_], H2[_], H3[_], L[_] <: CopK[_]]: Aux[AppendK[L, In2[H1, H2, ?], ?], H3, AppendK[L, In3[H1, H2, H3, ?], ?]] =
+ new AppendHK[AppendK[L, In2[H1, H2, ?], ?], H3] {
+ type Out[t] = AppendK[L, In3[H1, H2, H3, ?], t]
+
+ def apply[A](c: AppendK[L, In2[H1, H2, ?], A]): Out[A] = c match {
+ case Aplk(r) => Aplk(r)
+ case Aprk(In2l(h1)) => Aprk(In3l(h1))
+ case Aprk(In2r(h2)) => Aprk(In3m(h2))
+ }
+
+ def single[A](ha: H3[A]): Out[A] = Aprk(In3r(ha))
+
+ def nat[RR[_], A](out: AppendK[L, In3[H1, H2, H3, ?], A], nat1: AppendK[L, In2[H1, H2, ?], ?] ~> RR, nat2: H3 ~> RR): RR[A] = out match {
+ case Aplk(l) => nat1(Aplk(l))
+ case Aprk(In3l(h1)) => nat1(Aprk(In2l(h1)))
+ case Aprk(In3m(h2)) => nat1(Aprk(In2r(h2)))
+ case Aprk(In3r(h3)) => nat2(h3)
+ }
+ }
+
+}
+
+
+trait AppendHKLower {
+
+ implicit def append[H[_], L[_] <: CopK[_], R[_] <: CopK[_]]: AppendHK.Aux[AppendK[L, R, ?], H, AppendK[AppendK[L, R, ?], In1[H, ?], ?]] =
+ new AppendHK[AppendK[L, R, ?], H] {
+ type Out[t] = AppendK[AppendK[L, R, ?], In1[H, ?], t]
+
+ def apply[A](c: AppendK[L, R, A]): Out[A] = Aplk(c)
+
+ def single[A](ha: H[A]): Out[A] = Aprk(In1(ha))
+
+ def nat[RR[_], A](out: AppendK[AppendK[L, R, ?], In1[H, ?], A], nat1: AppendK[L, R, ?] ~> RR, nat2: H ~> RR): RR[A] = out match {
+ case Aplk(l) => nat1(l)
+ case Aprk(In1(h)) => nat2(h)
+ }
+ }
+
+
}
trait Replace[C[_] <: CopK[_], F[_], G[_]] {
@@ -466,18 +587,182 @@ trait ReplaceLower {
}
}
-// trait Flattener[F[_] <: CopK[_]] {
-// type Out[_] <: CopK[_]
-// def flatten[TC[_[_], _], F[_], A](t: TC[F, A]): TC[Out, A]
-// }
+trait Flattener[F[_] <: CopK[_], TC[_[_], _]] {
+ type Out[_] <: CopK[_]
+ def flatten[A](t: TC[F, A]): TC[Out, A]
+}
-// object Flattener {
+object Flattener extends FlattenerLower{
+ import cats.free.Free
+ type Aux[F[_] <: CopK[_], TC[_[_], _], Out0[_] <: CopK[_]] = Flattener[F, TC] { type Out[t] = Out0[t] }
-// type Aux[F[_] <: CopK[_], Out0[_] <: CopK[_]] = Flattener[F] { type Out[t] = Out0[t] }
+ implicit def in1[F[_] <: CopK[_]]: Flattener.Aux[In1[Free[F, ?], ?], Free, F] =
+ new Flattener[In1[Free[F, ?], ?], Free] {
+ type Out[t] = F[t]
-// implicit def one[F[_] <: CopK[_]]
-// }
+ def flatten[A](tca: Free[In1[Free[F, ?], ?], A]): Free[F, A] =
+ tca.foldMap(new (In1[Free[F, ?], ?] ~> Free[F, ?]) {
+ def apply[A](in: In1[Free[F, ?], A]): Free[F, A] = in match {
+ case In1(free) => free
+ }
+ })
+ }
+
+ implicit def in2Left[F[_] <: CopK[_], R[_], O[_] <: CopK[_]](
+ implicit ap: AppendHK.Aux[F, R, O]
+ ): Flattener.Aux[In2[Free[F, ?], R, ?], Free, O] =
+ new Flattener[In2[Free[F, ?], R, ?], Free] {
+ type Out[t] = O[t]
+
+ def flatten[A](tca: Free[In2[Free[F, ?], R, ?], A]): Free[O, A] =
+ tca.foldMap(new (In2[Free[F, ?], R, ?] ~> Free[O, ?]) {
+ def apply[A](in: In2[Free[F, ?], R, A]): Free[O, A] = in match {
+ case In2l(free) => free.compile(new (F ~> O) {
+ def apply[A](fa: F[A]): O[A] = ap(fa)
+ })
+
+ case In2r(r) => Free.liftF(ap.single(r))
+ }
+ })
+ }
+
+ implicit def in3Left[F[_] <: CopK[_], M[_], R[_], O1[_] <: CopK[_], O2[_] <: CopK[_]](
+ implicit ap1: AppendHK.Aux[F, M, O1]
+ , ap2: AppendHK.Aux[O1, R, O2]
+ ): Flattener.Aux[In3[Free[F, ?], M, R, ?], Free, O2] =
+ new Flattener[In3[Free[F, ?], M, R, ?], Free] {
+ type Out[t] = O2[t]
+
+ def flatten[A](tca: Free[In3[Free[F, ?], M, R, ?], A]): Free[O2, A] =
+ tca.foldMap(new (In3[Free[F, ?], M, R, ?] ~> Free[O2, ?]) {
+ def apply[A](in: In3[Free[F, ?], M, R, A]): Free[O2, A] = in match {
+ case In3l(free) => free.compile(new (F ~> O2) {
+ def apply[A](fa: F[A]): O2[A] = ap2(ap1(fa))
+ })
+
+ case In3m(m) => Free.liftF(ap2(ap1.single(m)))
+
+ case In3r(r) => Free.liftF(ap2.single(r))
+ }
+ })
+ }
+
+}
+
+trait FlattenerLower extends FlattenerLower2 {
+ import cats.free.Free
+
+ implicit def in2Right[F[_] <: CopK[_], L[_], O[_] <: CopK[_]](
+ implicit pr: PrependHK.Aux[L, F, O]
+ ): Flattener.Aux[In2[L, Free[F, ?], ?], Free, O] =
+ new Flattener[In2[L, Free[F, ?], ?], Free] {
+ type Out[t] = O[t]
+
+ def flatten[A](tca: Free[In2[L, Free[F, ?], ?], A]): Free[O, A] =
+ tca.foldMap(new (In2[L, Free[F, ?], ?] ~> Free[O, ?]) {
+ def apply[A](in: In2[L, Free[F, ?], A]): Free[O, A] = in match {
+ case In2l(l) => Free.liftF(pr.single(l))
+
+ case In2r(free) => free.compile(new (F ~> O) {
+ def apply[A](fa: F[A]): O[A] = pr(fa)
+ })
+ }
+ })
+ }
+
+ implicit def in3Middle[F[_] <: CopK[_], L[_], R[_], O1[_] <: CopK[_], O2[_] <: CopK[_]](
+ implicit pr: PrependHK.Aux[L, F, O1]
+ , ap: AppendHK.Aux[O1, R, O2]
+ ): Flattener.Aux[In3[L, Free[F, ?], R, ?], Free, O2] =
+ new Flattener[In3[L, Free[F, ?], R, ?], Free] {
+ type Out[t] = O2[t]
+
+ def flatten[A](tca: Free[In3[L, Free[F, ?], R, ?], A]): Free[O2, A] =
+ tca.foldMap(new (In3[L, Free[F, ?], R, ?] ~> Free[O2, ?]) {
+ def apply[A](in: In3[L, Free[F, ?], R, A]): Free[O2, A] = in match {
+ case In3l(l) => Free.liftF(ap(pr.single(l)))
+
+ case In3m(free) => free.compile(new (F ~> O2) {
+ def apply[A](fa: F[A]): O2[A] = ap(pr(fa))
+ })
+
+ case In3r(r) => Free.liftF(ap.single(r))
+ }
+ })
+ }
+
+
+ implicit def apkLeft[F[_] <: CopK[_], L[_] <: CopK[_], R[_] <: CopK[_], O[_] <: CopK[_]](
+ implicit flt: Flattener.Aux[L, Free, O]
+ ): Flattener.Aux[AppendK[L, R, ?], Free, AppendK[O, R, ?]] = new Flattener[AppendK[L, R, ?], Free] {
+ type Out[t] = AppendK[O, R, t]
+
+ def flatten[A](tca: Free[AppendK[L, R, ?], A]): Free[AppendK[O, R, ?], A] =
+
+ tca.foldMap(new (AppendK[L, R, ?] ~> Free[AppendK[O, R, ?], ?]) {
+ def apply[A](in: AppendK[L, R, A]): Free[AppendK[O, R, ?], A] = in match {
+ case Aplk(l) =>
+ val free = Free.liftF(l)
+ flt.flatten(free).compile(new (O ~> AppendK[O, R, ?]) {
+ def apply[A](oa: O[A]): AppendK[O, R, A] = Aplk(oa)
+ })
+
+ case Aprk(r) => Free.liftF(Aprk(r))
+ }
+ })
+ }
+
+}
+
+trait FlattenerLower2 {
+ import cats.free.Free
+
+ implicit def in3Right[F[_] <: CopK[_], L[_], M[_], O1[_] <: CopK[_], O2[_] <: CopK[_]](
+ implicit pr1: PrependHK.Aux[M, F, O1]
+ , pr2: PrependHK.Aux[L, O1, O2]
+ ): Flattener.Aux[In3[L, M, Free[F, ?], ?], Free, O2] =
+ new Flattener[In3[L, M, Free[F, ?], ?], Free] {
+ type Out[t] = O2[t]
+
+ def flatten[A](tca: Free[In3[L, M, Free[F, ?], ?], A]): Free[O2, A] =
+ tca.foldMap(new (In3[L, M, Free[F, ?], ?] ~> Free[O2, ?]) {
+ def apply[A](in: In3[L, M, Free[F, ?], A]): Free[O2, A] = in match {
+ case In3l(l) => Free.liftF(pr2.single(l))
+
+ case In3m(m) => Free.liftF(pr2(pr1.single(m)))
+
+ case In3r(free) => free.compile(new (F ~> O2) {
+ def apply[A](fa: F[A]): O2[A] = pr2(pr1(fa))
+ })
+
+ }
+ })
+ }
+
+
+ implicit def ApkRight[F[_] <: CopK[_], L[_] <: CopK[_], R[_] <: CopK[_], O[_] <: CopK[_]](
+ implicit flt: Flattener.Aux[R, Free, O]
+ ): Flattener.Aux[AppendK[L, R, ?], Free, AppendK[L, O, ?]] = new Flattener[AppendK[L, R, ?], Free] {
+ type Out[t] = AppendK[L, O, t]
+
+ def flatten[A](tca: Free[AppendK[L, R, ?], A]): Free[AppendK[L, O, ?], A] =
+
+ tca.foldMap(new (AppendK[L, R, ?] ~> Free[AppendK[L, O, ?], ?]) {
+ def apply[A](in: AppendK[L, R, A]): Free[AppendK[L, O, ?], A] = in match {
+ case Aplk(l) =>
+ Free.liftF(Aplk(l))
+
+ case Aprk(r) =>
+ val free = Free.liftF(r)
+ flt.flatten(free).compile(new (O ~> AppendK[L, O, ?]) {
+ def apply[A](oa: O[A]): AppendK[L, O, A] = Aprk(oa)
+ })
+
+ }
+ })
+ }
+}
// object CopAppend extends CopAppendLower {
diff --git a/src/main/scala/Freekit.scala b/src/main/scala/Freekit.scala
new file mode 100644
index 0000000..d92cccd
--- /dev/null
+++ b/src/main/scala/Freekit.scala
@@ -0,0 +1,58 @@
+package freek
+
+import scala.reflect.macros.{ blackbox, whitebox }
+import scala.reflect.macros.Context
+import scala.language.experimental.macros
+import scala.annotation.StaticAnnotation
+import scala.annotation.compileTimeOnly
+
+import cats.free.Free
+
+import scala.language.implicitConversions
+
+
+class Freekit[DSL0 <: DSL, C0[_] <: CopK[_]](val PRG: DSL.Make[DSL0, C0]) {
+ type PRG = PRG.DSL
+ type Cop[t] = PRG.Cop[t]
+
+ implicit def liftFA[F[_], A](fa: F[A])(
+ implicit sub0: SubCop[In1[F, ?], Cop]
+ ): Free[Cop, A] = {
+ Freek.expand[In1[F, ?], Cop, A](Freek(fa))(sub0)
+ }
+
+}
+
+class Freekito[DSL0 <: DSL, C0[_] <: CopK[_]](val PRG: DSL.Make[DSL0, C0]) {
+
+ type PRG = PRG.DSL
+ type Cop[t] = PRG.Cop[t]
+ type O <: Onion
+
+ implicit def liftFGHA[F[_], G[_], HA, A](fga: F[G[HA]])(
+ implicit
+ ga: HKK.Aux[G[HA], A]
+ , sub0: SubCop[In1[F, ?], PRG.Cop]
+ , lifter2: Lifter2.Aux[G[HA], O, A]
+ , pointer: Pointer[O]
+ , mapper: Mapper[O]
+ , binder: Binder[O]
+ , traverser: Traverser[O]
+ ): OnionT[Free, PRG.Cop, O, A] =
+ OnionT.liftTHK(Freek.expand[In1[F, ?], PRG.Cop, G[HA]](Freek(fga))(sub0))
+
+ implicit def liftFA[F[_], A](fa: F[A])(
+ implicit
+ sub0: SubCop[In1[F, ?], PRG.Cop]
+ , pointer: Pointer[O]
+ , mapper: Mapper[O]
+ , binder: Binder[O]
+ , traverser: Traverser[O]
+ ): OnionT[Free, PRG.Cop, O, A] =
+ toOnionT0(
+ Freek.expand[In1[F, ?], PRG.Cop, A](Freek(fa))(sub0)
+ ).onionT[O]
+
+}
+
+
diff --git a/src/main/scala/HasHoist.scala b/src/main/scala/HasHoist.scala
index b5be7e7..ae48a17 100644
--- a/src/main/scala/HasHoist.scala
+++ b/src/main/scala/HasHoist.scala
@@ -2,7 +2,7 @@ package freek
import scala.language.higherKinds
-import cats.data.{ OptionT, XorT, Xor }
+import cats.data.{ OptionT, EitherT }
import cats.Functor
import cats.free._
@@ -13,10 +13,10 @@ trait HasHoist[M[_]] {
def liftF[F[_] : Functor, A](f: F[A]): T[F, A]
}
-class XorHasHoist[A] extends HasHoist[λ[t => Xor[A, t]]] {
- type T[F[_], B] = XorT[F, A, B]
- def liftT[F[_], B](f: F[Xor[A, B]]): XorT[F, A, B] = XorT.apply(f)
- def liftF[F[_] : Functor, B](f: F[B]): XorT[F, A, B] = XorT.right(f)
+class EitherHasHoist[A] extends HasHoist[λ[t => Either[A, t]]] {
+ type T[F[_], B] = EitherT[F, A, B]
+ def liftT[F[_], B](f: F[Either[A, B]]): EitherT[F, A, B] = EitherT.apply(f)
+ def liftF[F[_] : Functor, B](f: F[B]): EitherT[F, A, B] = EitherT.right(f)
}
object HasHoist {
@@ -30,8 +30,8 @@ object HasHoist {
def liftF[F[_] : Functor, B](f: F[B]): OptionT[F, B] = OptionT.liftF(f)
}
- implicit def xorHasHoist[A]: HasHoist.Aux[λ[t => Xor[A, t]], λ[(f[_], b) => XorT[f, A, b]]] =
- new XorHasHoist[A]
+ implicit def eitherHasHoist[A]: HasHoist.Aux[λ[t => Either[A, t]], λ[(f[_], b) => EitherT[f, A, b]]] =
+ new EitherHasHoist[A]
}
diff --git a/src/main/scala/Interpreter.scala b/src/main/scala/Interpreter.scala
index 6fc9804..5d56e71 100644
--- a/src/main/scala/Interpreter.scala
+++ b/src/main/scala/Interpreter.scala
@@ -22,6 +22,15 @@ class Interpreter[C[_] <: CopK[_], R[_]](
// // TBD
// )
+ def :&&:[D[_] <: CopK[_]](f: Interpreter[D, R]): Interpreter[AppendK[C, D, ?], R] = new Interpreter(
+ new ~>[AppendK[C, D, ?], R] {
+ def apply[A](c: AppendK[C, D, A]): R[A] = c match {
+ case Aplk(l) => nat.nat(l)
+ case Aprk(r) => f.nat(r)
+ }
+ }
+ )
+
def andThen[R2[_]](r2: R ~> R2): Interpreter[C, R2] = new Interpreter(
nat andThen r2
)
diff --git a/src/main/scala/Onion.scala b/src/main/scala/Onion.scala
index e486368..066efad 100644
--- a/src/main/scala/Onion.scala
+++ b/src/main/scala/Onion.scala
@@ -372,4 +372,58 @@ trait Lifter2Low {
}
+}
+
+
+trait PartialLifter1[HA, S <: Onion] {
+ type GA
+ def partialLift(ha: HA): S#Layers[GA]
+}
+
+object PartialLifter1 {
+ type Aux[HA, S <: Onion, GA0] = PartialLifter1[HA, S] { type GA = GA0 }
+
+ def apply[HA, S <: Onion](implicit lifter: PartialLifter1[HA, S]) = lifter
+
+
+ implicit def fga[F[_], GA0, O <: Onion](
+ implicit lifter: Lifter[F, O]
+ ): PartialLifter1.Aux[F[GA0], O, GA0] = new PartialLifter1[F[GA0], O] {
+ type GA = GA0
+ def partialLift(fga: F[GA0]): O#Layers[GA0] = lifter.lift(fga)
+ }
+
+ // implicit def fga[F[_]: Functor, G[_], A0, O <: Onion](
+ // implicit lifter: Lifter[F, O]
+ // ): PartialLifter1.Aux[F[G[A0]], O, G[A0]] = new PartialLifter1[F[G[A0]], O] {
+ // type GA = G[A0]
+ // def partialLift(fga: F[G[A0]]): O#Layers[G[A0]] = lifter.lift(fga)
+ // }
+
+}
+
+trait PartialLifter2[HA, S <: Onion] {
+ type GA
+ def partialLift(ha: HA): S#Layers[GA]
+}
+
+object PartialLifter2 {
+ type Aux[HA, S <: Onion, GA0] = PartialLifter2[HA, S] { type GA = GA0 }
+
+ def apply[HA, S <: Onion](implicit lifter: PartialLifter2[HA, S]) = lifter
+
+ implicit def fga[F[_], G[_], HA0, O <: Onion](
+ implicit lifter: Lifter[λ[t => F[G[t]]], O]
+ ): PartialLifter2.Aux[F[G[HA0]], O, HA0] = new PartialLifter2[F[G[HA0]], O] {
+ type GA = HA0
+ def partialLift(fga: F[G[HA0]]): O#Layers[HA0] = lifter.lift(fga)
+ }
+
+ // implicit def fga[F[_]: Functor, G[_], A0, O <: Onion](
+ // implicit lifter: Lifter[F, O]
+ // ): PartialLifter1.Aux[F[G[A0]], O, G[A0]] = new PartialLifter1[F[G[A0]], O] {
+ // type GA = G[A0]
+ // def partialLift(fga: F[G[A0]]): O#Layers[G[A0]] = lifter.lift(fga)
+ // }
+
}
\ No newline at end of file
diff --git a/src/main/scala/OnionT.scala b/src/main/scala/OnionT.scala
index 4c12e6c..2e3887e 100644
--- a/src/main/scala/OnionT.scala
+++ b/src/main/scala/OnionT.scala
@@ -2,7 +2,6 @@ package freek
import cats.free.Free
import cats.{Applicative, Functor, FlatMap, Monad, Traverse, Eq}
-import cats.data.Xor
/** The OnionT transformer to manipulate monadic stack of results */
@@ -148,6 +147,26 @@ object OnionT extends OnionTInstances {
, traverser: Traverser[S]
): OnionT[TC, F, S, A] =
OnionT(tcMonad.map(fa){ fa => lifter2.lift2(fa) })
+
+ def liftTPartial1[TC[_[_], _], F[_], S <: Onion, GA, A](fa: TC[F, GA])(
+ implicit
+ tcMonad: Monad[TC[F, ?]]
+ , liftp: PartialLifter1[GA, S]
+ , mapper: Mapper[S]
+ , binder: Binder[S]
+ // , traverser: Traverser[S]
+ ): OnionT[TC, F, S, liftp.GA] =
+ OnionT(tcMonad.map(fa){ fa => liftp.partialLift(fa) })
+
+ def liftTPartial2[TC[_[_], _], F[_], S <: Onion, GA, A](fa: TC[F, GA])(
+ implicit
+ tcMonad: Monad[TC[F, ?]]
+ , liftp: PartialLifter2[GA, S]
+ , mapper: Mapper[S]
+ , binder: Binder[S]
+ // , traverser: Traverser[S]
+ ): OnionT[TC, F, S, liftp.GA] =
+ OnionT(tcMonad.map(fa){ fa => liftp.partialLift(fa) })
}
trait OnionTInstances {
@@ -168,7 +187,12 @@ trait OnionTInstances {
override def map[A, B](fa: OnionT[TC, F, S, A])(f: A => B): OnionT[TC, F, S, B] =
fa.map(f)
- def tailRecM[A, B](a: A)(f: A => OnionT[TC, F, S, Either[A, B]]): OnionT[TC, F, S, B] = defaultTailRecM(a)(f)
+ // unsafe
+ def tailRecM[A, B](a: A)(f: A => OnionT[TC, F, S, Either[A, B]]): OnionT[TC, F, S, B] =
+ f(a).flatMap {
+ case Left(nextA) => tailRecM(nextA)(f)
+ case Right(b) => pure(b)
+ }
}
diff --git a/src/main/scala/package.scala b/src/main/scala/package.scala
index 0e590c1..a2334e4 100644
--- a/src/main/scala/package.scala
+++ b/src/main/scala/package.scala
@@ -50,9 +50,27 @@ package object freek extends LowerImplicits with HK {
def interpret[F2[_] <: CopK[_], G[_]: Monad](i: Interpreter[F2, G])(
implicit sub:SubCop[F, F2]
- ): G[A] = free.foldMapUnsafe(new (F ~> G) {
+ ): G[A] = free.foldMap(new (F ~> G) {
def apply[A](fa: F[A]): G[A] = i.nat(sub(fa))
})
+
+ def flatten[O[_] <: CopK[_]](implicit flt: Flattener.Aux[F, Free, O]): Free[O, A] = flt.flatten(free)
+
+ def transpile[F2[_] <: CopK[_], G[_] <: CopK[_], O[_] <: CopK[_]](i: Interpreter[F2, G])(
+ implicit
+ sub:SubCop[F, F2]
+ , flt: Flattener.Aux[G, Free, O]
+ ): Free[O, A] = flt.flatten(free.compile(new (F ~> G) {
+ def apply[A](fa: F[A]): G[A] = i.nat(sub(fa))
+ }))
+
+ def transpile[F2[_] <: CopK[_], G[_] <: CopK[_], O[_] <: CopK[_]](i: F2 ~> G)(
+ implicit
+ sub:SubCop[F, F2]
+ , flt: Flattener.Aux[G, Free, O]
+ ): Free[O, A] = flt.flatten(free.compile(new (F ~> G) {
+ def apply[A](fa: F[A]): G[A] = i(sub(fa))
+ }))
}
implicit class FreeExtend[F[_], A](val free: Free[F, A]) extends AnyVal {
@@ -118,6 +136,23 @@ package object freek extends LowerImplicits with HK {
, traverser: Traverser[O]
): OnionT[TC, F, O, GA] = OnionT.liftP(tc)
+
+ @inline def onionX1[O <: Onion](
+ implicit
+ tcMonad: Monad[TC[F, ?]]
+ , liftp: PartialLifter1[GA, O]
+ , mapper: Mapper[O]
+ , binder: Binder[O]
+ ): OnionT[TC, F, O, liftp.GA] = OnionT.liftTPartial1(tc)
+
+ @inline def onionX2[O <: Onion](
+ implicit
+ tcMonad: Monad[TC[F, ?]]
+ , liftp: PartialLifter2[GA, O]
+ , mapper: Mapper[O]
+ , binder: Binder[O]
+ ): OnionT[TC, F, O, liftp.GA] = OnionT.liftTPartial2(tc)
+
}
implicit class toOnionExpand[C[_]<: CopK[_], O <: Onion, A](val onion: OnionT[Free, C, O, A]) {
@@ -183,7 +218,7 @@ package object freek extends LowerImplicits with HK {
}
package freek {
- trait LowerImplicits {
+ trait LowerImplicits extends LowerImplicits2 {
implicit class toOnionT0[TC[_[_], _], F[_], A](val tc: TC[F, A]) {
@inline def onionT[O <: Onion](
@@ -198,7 +233,11 @@ package freek {
}
- implicit def toInterpreter[F[_], R[_]](nat: F ~> R): Interpreter[In1[F, ?], R] = Interpreter(nat)
+ implicit def toInterpreterCopK[F[_] <: CopK[_], R[_]](nat: F ~> R): Interpreter[F, R] = new Interpreter(nat)
}
+
+ trait LowerImplicits2 {
+ implicit def toInterpreter[F[_], R[_]](nat: F ~> R): Interpreter[In1[F, ?], R] = Interpreter(nat)
+ }
}
\ No newline at end of file
diff --git a/src/test/scala/AppSpec.scala b/src/test/scala/AppSpec.scala
index 488a568..ec08521 100644
--- a/src/test/scala/AppSpec.scala
+++ b/src/test/scala/AppSpec.scala
@@ -7,7 +7,7 @@ package freek
import org.scalatest._
import cats.free.{Free, Trampoline}
-import cats.data.Xor
+// import cats.data.Either
import cats.{~>, Id}
import scala.concurrent._
@@ -18,6 +18,7 @@ import cats.Functor
import cats.instances.future._
import cats.instances.option._
import cats.instances.list._
+import cats.instances.either._
import ExecutionContext.Implicits.global
import freek._
@@ -55,7 +56,7 @@ object DB {
case object NotFound extends DBError
sealed trait DSL[A]
- case class FindById(id: String) extends DSL[Xor[DBError, Entity]]
+ case class FindById(id: String) extends DSL[Either[DBError, Entity]]
}
@@ -117,14 +118,14 @@ object Http {
case object NAck extends SendStatus
sealed trait HttpInteract[A]
- case object HttpReceive extends HttpInteract[Xor[RecvError, HttpReq]]
+ case object HttpReceive extends HttpInteract[Either[RecvError, HttpReq]]
case class HttpRespond(data: HttpResp) extends HttpInteract[SendStatus]
- case class Stop(error: Xor[RecvError, SendStatus]) extends HttpInteract[Xor[RecvError, SendStatus]]
+ case class Stop(error: Either[RecvError, SendStatus]) extends HttpInteract[Either[RecvError, SendStatus]]
object HttpInteract {
def receive() = HttpReceive
def respond(data: HttpResp) = HttpRespond(data)
- def stop(err: Xor[RecvError, SendStatus]) = Stop(err)
+ def stop(err: Either[RecvError, SendStatus]) = Stop(err)
}
sealed trait HttpHandle[A]
@@ -160,7 +161,7 @@ class AppSpec extends FlatSpec with Matchers {
val PRG = DSL.Make[PRG]
/** the DSL.Make */
- def findById(id: String): Free[PRG.Cop, Xor[DBError, Entity]] =
+ def findById(id: String): Free[PRG.Cop, Either[DBError, Entity]] =
for {
_ <- Log.debug("Searching for entity id:"+id).freek[PRG]
res <- FindById(id).freek[PRG]
@@ -185,8 +186,8 @@ class AppSpec extends FlatSpec with Matchers {
resp <- HttpHandle.result(
dbRes match {
- case Xor.Left(err) => HttpResp(status = InternalServerError)
- case Xor.Right(e) => HttpResp(status = Ok, body = e.toString)
+ case Left(err) => HttpResp(status = InternalServerError)
+ case Right(e) => HttpResp(status = Ok, body = e.toString)
}
).freek[PRG]
} yield (resp)
@@ -197,20 +198,20 @@ class AppSpec extends FlatSpec with Matchers {
// server DSL.Make
// this is the worst case: recursive call so need to help scalac a lot
// but in classic cases, it should be much more straighforward
- def serve() : Free[PRG.Cop, Xor[RecvError, SendStatus]] =
+ def serve() : Free[PRG.Cop, Either[RecvError, SendStatus]] =
for {
recv <- HttpInteract.receive().freek[PRG]
_ <- Log.info("HttpReceived Request:"+recv).freek[PRG]
res <- recv match {
- case Xor.Left(err) => HttpInteract.stop(Xor.left(err)).freek[PRG]
+ case Left(err) => HttpInteract.stop(Left(err)).freek[PRG]
- case Xor.Right(req) =>
+ case Right(req) =>
for {
resp <- handle(req)
_ <- Log.info("Sending Response:"+resp).freek[PRG]
ack <- HttpInteract.respond(resp).freek[PRG]
res <- if(ack == Ack) serve()
- else HttpInteract.stop(Xor.right(ack)).freek[PRG]
+ else HttpInteract.stop(Right(ack)).freek[PRG]
} yield (res)
}
} yield (res)
@@ -231,7 +232,7 @@ class AppSpec extends FlatSpec with Matchers {
def apply[A](a: DB.DSL[A]) = a match {
case DB.FindById(id) =>
println(s"DB Finding $id")
- Xor.right(Map("id" -> id, "name" -> "toto"))
+ Right(Map("id" -> id, "name" -> "toto"))
}
}
@@ -249,9 +250,9 @@ class AppSpec extends FlatSpec with Matchers {
case Http.HttpReceive =>
if(i < 10000) {
i+=1
- Xor.right(Http.GetReq("/foo"))
+ Right(Http.GetReq("/foo"))
} else {
- Xor.left(Http.ClientDisconnected)
+ Left(Http.ClientDisconnected)
}
case Http.HttpRespond(resp) => Http.Ack
@@ -285,18 +286,18 @@ class AppSpec extends FlatSpec with Matchers {
sealed trait Foo[A]
final case class Bar(s: String) extends Foo[Option[Int]]
- final case class Bar2(i: Int) extends Foo[Xor[String, Int]]
+ final case class Bar2(i: Int) extends Foo[Either[String, Int]]
final case object Bar3 extends Foo[Unit]
type PRG = Foo :|: Log.DSL :|: NilDSL
val PRG = DSL.Make[PRG]
val prg = for {
- i <- Bar("5").freek[PRG].liftT[Option].liftF[Xor[String, ?]]
- i <- Bar2(i).freek[PRG].liftF[Option].liftT[Xor[String, ?]]
- _ <- Log.info("toto " + i).freek[PRG].liftF[Option].liftF[Xor[String, ?]]
- _ <- Log.infoF("").expand[PRG].liftF[Option].liftF[Xor[String, ?]]
- _ <- Bar3.freek[PRG].liftF[Option].liftF[Xor[String, ?]]
+ i <- Bar("5").freek[PRG].liftT[Option].liftF[Either[String, ?]]
+ i <- Bar2(i).freek[PRG].liftF[Option].liftT[Either[String, ?]]
+ _ <- Log.info("toto " + i).freek[PRG].liftF[Option].liftF[Either[String, ?]]
+ _ <- Log.infoF("").expand[PRG].liftF[Option].liftF[Either[String, ?]]
+ _ <- Bar3.freek[PRG].liftF[Option].liftF[Either[String, ?]]
} yield (())
val logger2FutureSkip = new (Log.DSL ~> Future) {
@@ -309,7 +310,7 @@ class AppSpec extends FlatSpec with Matchers {
val foo2FutureSkip = new (Foo ~> Future) {
def apply[A](a: Foo[A]) = a match {
case Bar(s) => Future { Some(s.toInt) } // if you put None here, it stops prg before Log
- case Bar2(i) => Future(Xor.right(i))
+ case Bar2(i) => Future(Right(i))
case Bar3 => Future.successful(())
}
}
@@ -328,17 +329,17 @@ class AppSpec extends FlatSpec with Matchers {
sealed trait Foo[A]
final case class Foo1(s: String) extends Foo[List[Option[Int]]]
- final case class Foo2(i: Int) extends Foo[Xor[String, Int]]
+ final case class Foo2(i: Int) extends Foo[Either[String, Int]]
final case object Foo3 extends Foo[Unit]
- final case class Foo4(i: Int) extends Foo[Xor[String, Option[Int]]]
+ final case class Foo4(i: Int) extends Foo[Either[String, Option[Int]]]
sealed trait Bar[A]
final case class Bar1(s: String) extends Bar[Option[String]]
- final case class Bar2(i: Int) extends Bar[Xor[String, String]]
+ final case class Bar2(i: Int) extends Bar[Either[String, String]]
type PRG2 = Bar :|: Log.DSL :|: NilDSL
- type O = List :&: Xor[String, ?] :&: Option :&: Bulb
+ type O = List :&: Either[String, ?] :&: Option :&: Bulb
type PRG = Foo :|: Log.DSL :|: PRG2
val PRG = DSL.Make[PRG]
@@ -362,16 +363,16 @@ class AppSpec extends FlatSpec with Matchers {
val foo2Future = new (Foo ~> Future) {
def apply[A](a: Foo[A]) = a match {
case Foo1(s) => Future { List(Some(s.toInt)) } // if you put None here, it stops prg before Log
- case Foo2(i) => Future(Xor.right(i))
+ case Foo2(i) => Future(Right(i))
case Foo3 => Future.successful(())
- case Foo4(i) => Future.successful(Xor.right(Some(i)))
+ case Foo4(i) => Future.successful(Right(Some(i)))
}
}
val bar2Future = new (Bar ~> Future) {
def apply[A](a: Bar[A]) = a match {
case Bar1(s) => Future { Some(s) } // if you put None here, it stops prg before Log
- case Bar2(i) => Future(Xor.right(i.toString))
+ case Bar2(i) => Future(Right(i.toString))
}
}
@@ -389,17 +390,17 @@ class AppSpec extends FlatSpec with Matchers {
sealed trait Foo[A]
final case class Foo1(s: String) extends Foo[Option[Int]]
- final case class Foo2(i: Int) extends Foo[Xor[String, Int]]
+ final case class Foo2(i: Int) extends Foo[Either[String, Int]]
final case object Foo3 extends Foo[Unit]
- final case class Foo4(i: Int) extends Foo[Xor[String, Option[Int]]]
+ final case class Foo4(i: Int) extends Foo[Either[String, Option[Int]]]
sealed trait Bar[A]
final case class Bar1(s: String) extends Bar[List[Option[String]]]
- final case class Bar2(i: Int) extends Bar[Xor[String, String]]
+ final case class Bar2(i: Int) extends Bar[Either[String, String]]
type PRG2 = Bar :|: Log.DSL :|: NilDSL
- type O = List :&: Xor[String, ?] :&: Bulb
+ type O = List :&: Either[String, ?] :&: Bulb
type PRG = Foo :|: Log.DSL :|: PRG2
val PRG = DSL.Make[PRG]
@@ -426,16 +427,16 @@ class AppSpec extends FlatSpec with Matchers {
val foo2Future = new (Foo ~> Future) {
def apply[A](a: Foo[A]) = a match {
case Foo1(s) => Future { Some(s.toInt) } // if you put None here, it stops prg before Log
- case Foo2(i) => Future(Xor.right(i))
+ case Foo2(i) => Future(Right(i))
case Foo3 => Future.successful(())
- case Foo4(i) => Future.successful(Xor.right(Some(i)))
+ case Foo4(i) => Future.successful(Right(Some(i)))
}
}
val bar2Future = new (Bar ~> Future) {
def apply[A](a: Bar[A]) = a match {
case Bar1(s) => Future { List(Some(s)) } // if you put None here, it stops prg before Log
- case Bar2(i) => Future(Xor.right(i.toString))
+ case Bar2(i) => Future(Right(i.toString))
}
}
@@ -453,17 +454,17 @@ class AppSpec extends FlatSpec with Matchers {
sealed trait Foo[A]
final case class Foo1(s: String) extends Foo[Option[Int]]
- final case class Foo2(i: Int) extends Foo[Xor[String, Int]]
+ final case class Foo2(i: Int) extends Foo[Either[String, Int]]
final case object Foo3 extends Foo[Unit]
- final case class Foo4(i: Int) extends Foo[Xor[String, Option[Int]]]
+ final case class Foo4(i: Int) extends Foo[Either[String, Option[Int]]]
sealed trait Bar[A]
final case class Bar1(s: String) extends Bar[List[Option[String]]]
- final case class Bar2(i: Int) extends Bar[Xor[String, String]]
+ final case class Bar2(i: Int) extends Bar[Either[String, String]]
type PRG2 = Bar :|: Log.DSL :|: NilDSL
- type O = List :&: Xor[String, ?] :&: Option :&: Bulb
+ type O = List :&: Either[String, ?] :&: Option :&: Bulb
type PRG = Foo :|: Log.DSL :|: PRG2
val PRG = DSL.Make[PRG]
@@ -493,16 +494,16 @@ class AppSpec extends FlatSpec with Matchers {
val foo2Future = new (Foo ~> Future) {
def apply[A](a: Foo[A]) = a match {
case Foo1(s) => Future { Some(s.toInt) } // if you put None here, it stops prg before Log
- case Foo2(i) => Future(Xor.right(i))
+ case Foo2(i) => Future(Right(i))
case Foo3 => Future.successful(())
- case Foo4(i) => Future.successful(Xor.right(Some(i)))
+ case Foo4(i) => Future.successful(Right(Some(i)))
}
}
val bar2Future = new (Bar ~> Future) {
def apply[A](a: Bar[A]) = a match {
case Bar1(s) => Future { List(Some(s)) } // if you put None here, it stops prg before Log
- case Bar2(i) => Future(Xor.right(i.toString))
+ case Bar2(i) => Future(Right(i.toString))
}
}
@@ -525,17 +526,17 @@ class AppSpec extends FlatSpec with Matchers {
sealed trait Foo[A]
final case class Foo1(s: String) extends Foo[List[Option[Int]]]
- final case class Foo2(i: Int) extends Foo[Xor[String, Int]]
+ final case class Foo2(i: Int) extends Foo[Either[String, Int]]
final case object Foo3 extends Foo[Unit]
- final case class Foo4(i: Int) extends Foo[Xor[String, Option[Int]]]
+ final case class Foo4(i: Int) extends Foo[Either[String, Option[Int]]]
sealed trait Bar[A]
final case class Bar1(s: String) extends Bar[Option[String]]
- final case class Bar2(i: Int) extends Bar[Xor[String, String]]
+ final case class Bar2(i: Int) extends Bar[Either[String, String]]
type PRG2 = Bar :|: Log.DSL :|: NilDSL
- type O = List :&: Xor[String, ?] :&: Option :&: Bulb
+ type O = List :&: Either[String, ?] :&: Option :&: Bulb
type PRG = Foo :|: Log.DSL :|: KVS[String, Int, ?] :|: PRG2
val PRG = DSL.Make[PRG]
@@ -561,16 +562,16 @@ class AppSpec extends FlatSpec with Matchers {
val foo2Future = new (Foo ~> Future) {
def apply[A](a: Foo[A]) = a match {
case Foo1(s) => Future { List(Some(s.toInt)) } // if you put None here, it stops prg before Log
- case Foo2(i) => Future(Xor.right(i))
+ case Foo2(i) => Future(Right(i))
case Foo3 => Future.successful(())
- case Foo4(i) => Future.successful(Xor.right(Some(i)))
+ case Foo4(i) => Future.successful(Right(Some(i)))
}
}
val bar2Future = new (Bar ~> Future) {
def apply[A](a: Bar[A]) = a match {
case Bar1(s) => Future { Some(s) } // if you put None here, it stops prg before Log
- case Bar2(i) => Future(Xor.right(i.toString))
+ case Bar2(i) => Future(Right(i.toString))
}
}
@@ -585,8 +586,7 @@ class AppSpec extends FlatSpec with Matchers {
val interpreters = foo2Future :&: logger2Future :&: bar2Future :&: kvs2Future
- Await.result(prg.value.interpret(interpreters), 10.seconds)
-
+ Await.result(prg.value.interpret(interpreters), 10.seconds)
}
"freek" should "manage monadic onions of result types wrap/peelRight" in {
@@ -596,19 +596,19 @@ class AppSpec extends FlatSpec with Matchers {
sealed trait Bar[A]
final case class Bar1(s: String) extends Bar[List[Option[String]]]
- final case class Bar2(i: Int) extends Bar[Xor[String, String]]
+ final case class Bar2(i: Int) extends Bar[Either[String, String]]
type PRG2 = Bar :|: Log.DSL :|: NilDSL
- type O = List :&: Xor[String, ?] :&: Option :&: Bulb
+ type O = List :&: Either[String, ?] :&: Option :&: Bulb
type PRG = Foo :|: Log.DSL :|: PRG2
val PRG = DSL.Make[PRG]
- val f: OnionT[Free, PRG.Cop, List :&: Xor[String, ?] :&: Bulb, Option[Int]] =
+ val f: OnionT[Free, PRG.Cop, List :&: Either[String, ?] :&: Bulb, Option[Int]] =
Foo1("5")
.freek[PRG]
- .onionT[Xor[String, ?] :&: Option :&: Bulb]
+ .onionT[Either[String, ?] :&: Option :&: Bulb]
.wrap[List]
.peelRight
@@ -622,17 +622,17 @@ class AppSpec extends FlatSpec with Matchers {
sealed trait Foo[A]
final case class Foo1(s: String) extends Foo[List[Option[Int]]]
- final case class Foo2(i: Int) extends Foo[Xor[String, Int]]
+ final case class Foo2(i: Int) extends Foo[Either[String, Int]]
final case object Foo3 extends Foo[Unit]
- final case class Foo4(i: Int) extends Foo[Xor[String, Option[Int]]]
+ final case class Foo4(i: Int) extends Foo[Either[String, Option[Int]]]
sealed trait Bar[A]
final case class Bar1(s: String) extends Bar[Option[String]]
- final case class Bar2(i: Int) extends Bar[Xor[String, String]]
+ final case class Bar2(i: Int) extends Bar[Either[String, String]]
type PRG2 = Bar :|: Log.DSL :|: NilDSL
- type O = List :&: Xor[String, ?] :&: Option :&: Bulb
+ type O = List :&: Either[String, ?] :&: Option :&: Bulb
type PRG = Foo :|: Log.DSL :|: PRG2
val PRG = DSL.Make[PRG]
@@ -656,16 +656,16 @@ class AppSpec extends FlatSpec with Matchers {
val foo2Future = new (Foo ~> Future) {
def apply[A](a: Foo[A]) = a match {
case Foo1(s) => Future { List(Some(s.toInt)) } // if you put None here, it stops prg before Log
- case Foo2(i) => Future(Xor.right(i))
+ case Foo2(i) => Future(Right(i))
case Foo3 => Future.successful(())
- case Foo4(i) => Future.successful(Xor.right(Some(i)))
+ case Foo4(i) => Future.successful(Right(Some(i)))
}
}
val bar2Future = new (Bar ~> Future) {
def apply[A](a: Bar[A]) = a match {
case Bar1(s) => Future { Some(s) } // if you put None here, it stops prg before Log
- case Bar2(i) => Future(Xor.right(i.toString))
+ case Bar2(i) => Future(Right(i.toString))
}
}
@@ -683,13 +683,13 @@ class AppSpec extends FlatSpec with Matchers {
sealed trait RepoF[A]
sealed trait Repo[A]
- case class Query(no: String) extends Repo[Xor[String, Account]]
- case class Store(account: Account) extends Repo[Xor[String, Account]]
- case class Delete(no: String) extends Repo[Xor[String, Unit]]
+ case class Query(no: String) extends Repo[Either[String, Account]]
+ case class Store(account: Account) extends Repo[Either[String, Account]]
+ case class Delete(no: String) extends Repo[Either[String, Unit]]
object Repo {
type PRG = Repo :|: NilDSL
- type O = Xor[String, ?] :&: Bulb
+ type O = Either[String, ?] :&: Bulb
}
def query(no: String) = Query(no)
@@ -706,9 +706,9 @@ class AppSpec extends FlatSpec with Matchers {
trait FooLayer extends RepositoryLayer {
sealed trait Foo[A]
final case class Foo1(s: String) extends Foo[List[Option[Int]]]
- final case class Foo2(i: Int) extends Foo[Xor[String, Int]]
+ final case class Foo2(i: Int) extends Foo[Either[String, Int]]
final case object Foo3 extends Foo[Unit]
- final case class Foo4(i: Int) extends Foo[Xor[String, Option[Int]]]
+ final case class Foo4(i: Int) extends Foo[Either[String, Option[Int]]]
object Foo {
type PRG = Foo :|: Log.DSL :|: Repo.PRG
@@ -719,7 +719,7 @@ class AppSpec extends FlatSpec with Matchers {
sealed trait Bar[A]
final case class Bar1(s: String) extends Bar[Option[String]]
- final case class Bar2(i: Int) extends Bar[Xor[String, String]]
+ final case class Bar2(i: Int) extends Bar[Either[String, String]]
object Bar {
type PRG = Bar :|: Log.DSL :|: Repo.PRG
@@ -731,7 +731,7 @@ class AppSpec extends FlatSpec with Matchers {
extends FooLayer
with BarLayer {
- type O = List :&: Xor[String, ?] :&: Option :&: Bulb
+ type O = List :&: Either[String, ?] :&: Option :&: Bulb
type PRG = Log.DSL :|: Bar.PRG :||: Foo.PRG
val PRG = DSL.Make[PRG]
@@ -756,31 +756,37 @@ class AppSpec extends FlatSpec with Matchers {
val foo2Future = new (Foo ~> Future) {
def apply[A](a: Foo[A]) = a match {
case Foo1(s) => Future { println(s); List(Some(s.toInt)) } // if you put None here, it stops prg before Log
- case Foo2(i) => Future(Xor.right(i))
+ case Foo2(i) => Future(Right(i))
case Foo3 => Future.successful(())
- case Foo4(i) => Future.successful(Xor.right(Some(i)))
+ case Foo4(i) => Future.successful(Right(Some(i)))
}
}
val bar2Future = new (Bar ~> Future) {
def apply[A](a: Bar[A]) = a match {
case Bar1(s) => Future { Some(s) } // if you put None here, it stops prg before Log
- case Bar2(i) => Future(Xor.right(i.toString))
+ case Bar2(i) => Future(Right(i.toString))
}
}
val repo2Future = new (Repo ~> Future) {
def apply[A](a: Repo[A]) = a match {
- case Query(s) => Future { Xor.right(new Account {}) }
- case Store(acc) => Future { Xor.right(new Account {}) }
- case Delete(no) => Future { Xor.right(()) }
+ case Query(s) => Future { Right(new Account {}) }
+ case Store(acc) => Future { Right(new Account {}) }
+ case Delete(no) => Future { Right(()) }
}
}
+ val fooInterpreters = foo2Future :&: logger2Future :&: repo2Future
+ val barInterpreters = bar2Future :&: logger2Future :&: repo2Future
+
val interpreters = foo2Future :&: logger2Future :&: bar2Future :&: repo2Future
+ val interpreters2 = logger2Future :&: fooInterpreters :&&: barInterpreters
}
val r = Await.result(Prg.prg.value.interpret(Prg.interpreters), 10.seconds)
println("result:"+r)
+ val r2 = Await.result(Prg.prg.value.interpret(Prg.interpreters2), 10.seconds)
+ println("result:"+r2)
}
@@ -821,15 +827,15 @@ class AppSpec extends FlatSpec with Matchers {
final case class Bar22(s: Int) extends Foo2[List[Option[Int]]]
sealed trait Foo3[A]
- final case class Bar31(s: Long) extends Foo3[Xor[String, Long]]
- final case class Bar32(s: Float) extends Foo3[Xor[String, List[Float]]]
- final case class Bar33(s: Double) extends Foo3[Xor[String, Option[Boolean]]]
+ final case class Bar31(s: Long) extends Foo3[Either[String, Long]]
+ final case class Bar32(s: Float) extends Foo3[Either[String, List[Float]]]
+ final case class Bar33(s: Double) extends Foo3[Either[String, Option[Boolean]]]
type PRG = Foo1 :|: Foo2 :|: Foo3 :|: NilDSL
val PRG = DSL.Make[PRG]
- type O = Xor[String, ?] :&: List :&: Option :&: Bulb
+ type O = Either[String, ?] :&: List :&: Option :&: Bulb
- val f1: Free[PRG.Cop, Xor[String, List[Option[Unit]]]] = (for {
+ val f1: Free[PRG.Cop, Either[String, List[Option[Unit]]]] = (for {
i <- Bar1(3).freek[PRG].onionT[O]
i <- Bar21(i).freek[PRG].onionT[O]
i <- Bar22(i).freek[PRG].onionT[O]
@@ -849,17 +855,17 @@ class AppSpec extends FlatSpec with Matchers {
final case class Bar22(s: Int) extends Foo2[List[Option[Int]]]
sealed trait Foo3[A]
- final case class Bar31(s: Int) extends Foo3[Xor[String, Long]]
- final case class Bar32(s: Float) extends Foo3[Xor[String, List[Float]]]
- final case class Bar33(s: Double) extends Foo3[Xor[String, Option[Boolean]]]
- final case class Bar34(s: Double) extends Foo3[Xor[String, List[Option[Boolean]]]]
+ final case class Bar31(s: Int) extends Foo3[Either[String, Long]]
+ final case class Bar32(s: Float) extends Foo3[Either[String, List[Float]]]
+ final case class Bar33(s: Double) extends Foo3[Either[String, Option[Boolean]]]
+ final case class Bar34(s: Double) extends Foo3[Either[String, List[Option[Boolean]]]]
type PRG = Foo1 :|: Foo2 :|: Foo3 :|: NilDSL
val PRG = DSL.Make[PRG]
- type O = Xor[String, ?] :&: List :&: Option :&: Bulb
+ type O = Either[String, ?] :&: List :&: Option :&: Bulb
// ugly head & get :D
- val f1: Free[PRG.Cop, Xor[String, String]] = (for {
+ val f1: Free[PRG.Cop, Either[String, String]] = (for {
i <- Bar1(3).freek[PRG].onionT2[O]
i <- Bar21(i.head.get).freek[PRG].onionT2[O]
i <- Bar22(i.head.get).freek[PRG].onionT2[O]
@@ -870,5 +876,298 @@ class AppSpec extends FlatSpec with Matchers {
}
+ "freek" should "special cases 4" in {
+ sealed trait Foo1[A]
+ final case class Bar11(s: Int) extends Foo1[Either[String, List[Int]]]
+ final case class Bar12(s: List[Int]) extends Foo1[Either[String, Option[Int]]]
+
+ sealed trait Foo2[A]
+ final case class Bar21(s: Int) extends Foo1[Either[Long, Option[List[Int]]]]
+ final case class Bar22(s: List[Int]) extends Foo1[Either[Long, Option[Int]]]
+
+ type PRG = Foo1 :|: Foo2 :|: NilDSL
+ val PRG = DSL.Make[PRG]
+ type O = Either[String, ?] :&: Either[Long, ?] :&: Option :&: Bulb
+
+ val f1: OnionT[Free, PRG.Cop, O, Unit] = for {
+ l1 <- Bar11(5).freek[PRG].onionX1[O]
+ _ <- Bar12(l1).freek[PRG].onionT[O]
+ l2 <- Bar21(6).freek[PRG].onionX2[O]
+ _ <- Bar22(l2).freek[PRG].onionT[O]
+ } yield (())
+
+ }
+
+ "freek" should "extend DSL" in {
+ object Program {
+ sealed trait Foo1[A]
+ final case class Bar11(s: Int) extends Foo1[String]
+
+ sealed trait Foo2[A]
+ final case class Bar21(s: String) extends Foo2[Int]
+
+ type PRG = Foo1 :|: Foo2 :|: NilDSL
+ val PRG = DSL.Make[PRG]
+
+ val program = for {
+ s <- Bar11(5).freek[PRG]
+ i <- Bar21(s).freek[PRG]
+ } yield (i)
+ }
+
+ object OtherProgram {
+ import Program._
+
+ sealed trait Foo3[A]
+ case class Bar31[A](bar11: Foo1[A]) extends Foo3[A]
+ case class Bar32(i: Int) extends Foo3[String]
+
+ type PRG = Foo3 :|: Foo2 :|: NilDSL
+ val PRG = DSL.Make[PRG]
+
+ val copknat = CopKNat[Program.PRG.Cop].replace(
+ new (Foo1 ~> Foo3) {
+ def apply[A](foo1: Foo1[A]): Foo3[A] = Bar31(foo1)
+ }
+ )
+
+ val program = for {
+ i <- Program.program.compile(copknat)
+ s <- Bar32(i).freek[PRG]
+ } yield (s)
+
+ }
+
+ import Program._
+ import OtherProgram._
+
+ val foo1Future = new (Foo1 ~> Future) {
+ def apply[A](a: Foo1[A]) = a match {
+ case Bar11(i) => Future { i.toString }
+ }
+ }
+
+ val foo2Future = new (Foo2 ~> Future) {
+ def apply[A](a: Foo2[A]) = a match {
+ case Bar21(s) => Future { s.toInt }
+ }
+ }
+
+ def foo3Future(foo1Nat: Foo1 ~> Future) = new (Foo3 ~> Future) {
+ def apply[A](a: Foo3[A]) = a match {
+ case Bar31(foo1) => foo1Nat(foo1)
+ case Bar32(i) => Future { i.toString }
+ }
+ }
+
+ val interpreter = foo2Future :&: foo3Future(foo1Future)
+
+ val fut = OtherProgram.program.interpret(interpreter)
+
+ ()
+ }
+
+ "freek" should "append DSL" in {
+ object Program {
+ sealed trait Foo1[A]
+ final case class Bar11(s: Int) extends Foo1[String]
+
+ sealed trait Foo2[A]
+ final case class Bar21(s: String) extends Foo2[Int]
+
+ type PRG = Foo1 :|: Foo2 :|: NilDSL
+ val PRG = DSL.Make[PRG]
+
+ val program = for {
+ s <- Bar11(5).freek[PRG]
+ i <- Bar21(s).freek[PRG]
+ } yield (i)
+ }
+
+ object OtherProgram {
+ import Program._
+
+ sealed trait Foo3[A]
+ case class Bar31[A](bar11: Foo1[A]) extends Foo3[A]
+ case class Bar32(i: Int) extends Foo3[String]
+
+ type PRG = Foo3 :|: Foo2 :|: NilDSL
+ val PRG = DSL.Make[PRG]
+
+ val copknat = CopKNat[Program.PRG.Cop].replace(
+ new (Foo1 ~> Foo3) {
+ def apply[A](foo1: Foo1[A]): Foo3[A] = Bar31(foo1)
+ }
+ )
+
+ val program = for {
+ i <- Program.program.compile(copknat)
+ s <- Bar32(i).freek[PRG]
+ } yield (s)
+
+ }
+
+ import Program._
+ import OtherProgram._
+
+ val foo1Future = new (Foo1 ~> Future) {
+ def apply[A](a: Foo1[A]) = a match {
+ case Bar11(i) => Future { i.toString }
+ }
+ }
+
+ val foo2Future = new (Foo2 ~> Future) {
+ def apply[A](a: Foo2[A]) = a match {
+ case Bar21(s) => Future { s.toInt }
+ }
+ }
+
+ def foo3Future(foo1Nat: Foo1 ~> Future) = new (Foo3 ~> Future) {
+ def apply[A](a: Foo3[A]) = a match {
+ case Bar31(foo1) => foo1Nat(foo1)
+ case Bar32(i) => Future { i.toString }
+ }
+ }
+
+ val interpreter = foo2Future :&: foo3Future(foo1Future)
+
+ val fut = OtherProgram.program.interpret(interpreter)
+
+ ()
+ }
+
+ "freek" should "transpile" in {
+ object Program {
+ sealed trait Foo1[A]
+ final case class Bar11(s: Int) extends Foo1[Int]
+
+ sealed trait Foo2[A]
+ final case class Bar21(s: String) extends Foo2[String]
+
+ type PRG = Foo1 :|: Foo2 :|: NilDSL
+ val PRG = DSL.Make[PRG]
+
+ val program = for {
+ _ <- Bar11(5).freek[PRG]
+ _ <- Bar21("1.234").freek[PRG]
+ } yield (())
+ }
+
+ object OtherProgram {
+
+ sealed trait Foo3[A]
+ final case class Bar31(s: String) extends Foo3[Float]
+
+ sealed trait Foo4[A]
+ final case class Bar41(s: Float) extends Foo4[String]
+
+ type PRG = Foo3 :|: Foo4 :|: NilDSL
+ val PRG = DSL.Make[PRG]
+
+ // this is our transpiler transforming a Foo2 into another free program
+ val transpiler = new (Program.Foo2 ~> Free[PRG.Cop, ?]) {
+
+ def apply[A](f: Program.Foo2[A]): Free[PRG.Cop, A] = f match {
+ case Program.Bar21(s) =>
+ for {
+ f <- Bar31(s).freek[PRG]
+ s <- Bar41(f).freek[PRG]
+ } yield (s)
+ }
+ }
+ }
+
+ import Program._
+ import OtherProgram._
+
+ // 1/ CopKNat[Program.PRG.Cop] creates a Program.PRG.Cop ~> Program.PRG.Cop
+ // 2/ .replace creates a natural trans that replaces Program.Foo2 in Program.PRG.Cop by Free[OtherProgram.PRG.Cop, ?] using transpiler
+ // 3/ The result is a terrible natural transformation (don't try to write that type, it's too ugly, let's scalac do it) :
+ // (Foo1 :|: Foo2 :|: NilDSL) ~> (Foo1 :|: Free[OtherProgram.PRG.Cop, ?] :|: NilDSL)
+ val transpileNat = CopKNat[Program.PRG.Cop].replace(OtherProgram.transpiler)
+
+ // Transpile does 2 operations:
+ // 1/ Replaces Foo2 in Program.PRG.Cop by Free[OtherProgram.PRG.Cop, A]
+ // -> OtherProgram.transpiler natural transformation converts Foo2 into the free program Free[OtherProgram.PRG.Cop, A]
+ // -> New PRG.Cop is then Foo1 :|: Free[OtherProgram.PRG.Cop, ?] :|: NilDSL
+ //
+ // 2/ Flattens Free[(Foo1 :|: Free[(Foo3 :|: Foo4 :|: NilDSL)#Cop, ?] :|: NilDSL)#Cop, A] into
+ // Free[(Foo1 :|: Foo3 :|: Foo4 :|: NilDSL)#Cop, A]
+ val free = Program.program.transpile(transpileNat)
+ // Same as
+ // val free2 = Program.f.compile(transpileNat).flatten
+
+ // Write our interpreters for new program (Foo1, Foo3, Foo4)
+ val foo1Future = new (Foo1 ~> Future) {
+ def apply[A](a: Foo1[A]) = a match {
+ case Bar11(i) => Future { i }
+ }
+ }
+
+ val foo3Future = new (Foo3 ~> Future) {
+ def apply[A](a: Foo3[A]) = a match {
+ case Bar31(s) => Future { s.toFloat }
+ }
+ }
+
+ val foo4Future = new (Foo4 ~> Future) {
+ def apply[A](a: Foo4[A]) = a match {
+ case Bar41(s) => Future { s.toString }
+ }
+ }
+
+ val r = Await.result(free.interpret(foo1Future :&: foo3Future :&: foo4Future), 2.seconds)
+ println("r:"+r)
+ ()
+ }
+
+ // "freek" should "special case" in {
+ // import java.io.File
+
+ // sealed trait KVS[A]
+ // object KVS {
+ // case class Get(key: String) extends KVS[String]
+ // case class Put(key: String, value: String) extends KVS[Unit]
+ // }
+
+ // sealed trait FileIO[A]
+ // object FileIO {
+ // case class Get(name: String) extends FileIO[File]
+ // case class Delete(name: String) extends FileIO[Unit]
+ // }
+
+ // val FileInterpreter = new (FileIO ~> Lambda[A => Future[Either[Exception, A]]]) {
+ // override def apply[A](fa: FileIO[A]): Future[Either[Exception, A]] = fa match {
+ // case FileIO.Get(name) =>
+ // Future {
+ // Right(new File(name))
+ // }
+
+ // case FileIO.Delete(name) =>
+ // Future {
+ // new File(name).delete()
+ // Right(())
+ // }
+ // }
+ // }
+
+ // val KVSInterpreter = new (KVS ~> Lambda[A => Future[Either[Exception, A]]]) {
+ // def apply[A](a: KVS[A]): Future[Either[Exception, A]] = a match {
+ // case KVS.Get(id) =>
+ // Future {
+ // Right("123")
+ // }
+ // case KVS.Put(id, value) =>
+ // Future {
+ // Right(())
+ // }
+ // }
+ // }
+
+
+ // val interpreter = KVSInterpreter :&: toInterpreter(FileInterpreter)
+
+ // }
+
}
diff --git a/src/test/scala/FreekitSpec.scala b/src/test/scala/FreekitSpec.scala
new file mode 100644
index 0000000..f6c37bc
--- /dev/null
+++ b/src/test/scala/FreekitSpec.scala
@@ -0,0 +1,211 @@
+package freek
+
+
+/**
+ * Copyright 2014 Pascal Voitot (@mandubian)
+ */
+import org.scalatest._
+
+import cats.free.{Free, Trampoline}
+import cats.{~>, Id}
+
+import scala.concurrent._
+import scala.concurrent.duration._
+
+// import cats.derived._, functor._, legacy._
+import cats.Functor
+import cats.instances.future._
+import cats.instances.option._
+import cats.instances.list._
+import cats.instances.either._
+import ExecutionContext.Implicits.global
+
+import freek._
+
+
+class FreekitSpec extends FlatSpec with Matchers {
+
+ "Freek" should "macro" in {
+
+ //////////////////////////////////////////////////////////////////////////
+ // LOG DSL
+ sealed trait Log[A]
+ object Log {
+ case class Info(msg: String) extends Log[Unit]
+ }
+
+ sealed trait Foo1[A]
+ object Foo1 {
+ final case class Bar1(a: Int) extends Foo1[Option[Int]]
+ }
+
+ sealed trait Foo2[A]
+ object Foo2 {
+ final case class Bar21(a: Int) extends Foo2[Int]
+ final case object Bar22 extends Foo2[Int]
+ }
+
+
+ type PRG = Foo1 :|: Foo2 :|: Log :|: NilDSL
+ val PRG = DSL.Make[PRG]
+
+ object M extends Freekit(PRG) {
+ val prg = for {
+ aOpt <- Foo1.Bar1(7)
+ _ <- Log.Info(s"aOpt:$aOpt")
+ a <- aOpt match {
+ case Some(a) => for {
+ a <- Foo2.Bar21(a)
+ _ <- Log.Info(s"a1:$a")
+ } yield (a)
+ case None => for {
+ a <- Foo2.Bar22
+ _ <- Log.Info(s"a2:$a")
+ } yield (a)
+ }
+ } yield (a)
+ }
+
+ object MO extends Freekito(PRG) {
+ type O = Option :&: Bulb
+
+ val prg = for {
+ a <- Foo1.Bar1(7)
+ _ <- Log.Info(s"a:$a")
+ a <- Foo2.Bar21(a)
+ } yield (a)
+ }
+
+ val foo1I = new (Foo1 ~> Future) {
+ import Foo1._
+
+ def apply[A](f: Foo1[A]): Future[A] = f match {
+ case Bar1(a) => Future(Some(a))
+
+ }
+ }
+
+ val foo2I = new (Foo2 ~> Future) {
+ import Foo2._
+
+ def apply[A](f: Foo2[A]): Future[A] = f match {
+ case Bar21(a) => Future(a)
+ case Bar22 => Future(0)
+ }
+ }
+
+
+ val logI = new (Log ~> Future) {
+ def apply[A](a: Log[A]) = a match {
+ case Log.Info(msg) =>
+ Future.successful(println(s"[info] $msg"))
+ }
+ }
+
+ val f = M.prg.interpret(foo1I :&: foo2I :&: logI)
+ Await.result(f, 10.seconds)
+
+ val f2 = MO.prg.value.interpret(foo1I :&: foo2I :&: logI)
+ Await.result(f2, 10.seconds)
+ }
+
+
+ "Freekit" should "special cases 4" in {
+ sealed trait Foo1[A]
+ final case class Bar11(s: Int) extends Foo1[Either[String, List[Int]]]
+ final case class Bar12(s: List[Int]) extends Foo1[Either[String, Option[Int]]]
+
+ sealed trait Foo2[A]
+ final case class Bar21(s: Int) extends Foo1[Either[Long, Option[List[Int]]]]
+ final case class Bar22(s: List[Int]) extends Foo1[Either[Long, Option[Int]]]
+
+ type PRG = Foo1 :|: Foo2 :|: NilDSL
+ val PRG = DSL.Make[PRG]
+
+ object F1 extends Freekito(PRG) {
+ type O = Either[String, ?] :&: Either[Long, ?] :&: Option :&: Bulb
+
+ val prg = for {
+ l1 <- Bar11(5).freek[PRG].onionX1[O]
+ _ <- Bar12(l1)
+ l2 <- Bar21(6).freek[PRG].onionX2[O]
+ _ <- Bar22(l2)
+ } yield (())
+ }
+ }
+
+ "Freekit" should "freek" in {
+ import Http._
+ import DB._
+
+ object DBService extends Freekit(DSL.Make[Log.DSL :|: DB.DSL :|: NilDSL]) {
+
+ /** the DSL.Make */
+ def findById(id: String): Free[PRG.Cop, Either[DBError, Entity]] =
+ for {
+ _ <- Log.debug("Searching for entity id:"+id)
+ res <- FindById(id)
+ _ <- Log.debug("Search result:"+res)
+ } yield (res)
+ }
+
+ object HttpService extends Freekit(DSL.Make[Log.DSL :|: HttpInteract :|: HttpHandle :|: DBService.PRG]) {
+
+ def handle(req: HttpReq): Free[PRG.Cop, HttpResp] = req.url match {
+ case "/foo" =>
+ for {
+ _ <- Log.debug("/foo")
+ dbRes <- DBService.findById("foo").expand[PRG]
+
+ resp <- HttpHandle.result(
+ dbRes match {
+ case Left(err) => HttpResp(status = InternalServerError)
+ case Right(e) => HttpResp(status = Ok, body = e.toString)
+ }
+ )
+ } yield (resp)
+
+ case _ => HttpHandle.result(HttpResp(status = InternalServerError))
+ }
+
+ def serve() : Free[PRG.Cop, Either[RecvError, SendStatus]] =
+ for {
+ recv <- HttpInteract.receive()
+ _ <- Log.info("HttpReceived Request:"+recv)
+ res <- recv match {
+ case Left(err) => HttpInteract.stop(Left(err)).freek[PRG]
+
+ case Right(req) =>
+ for {
+ resp <- handle(req)
+ _ <- Log.info("Sending Response:"+resp)
+ ack <- HttpInteract.respond(resp)
+ res <- if(ack == Ack) serve()
+ else HttpInteract.stop(Right(ack)).freek[PRG]
+ } yield (res)
+ }
+ } yield (res)
+
+ }
+ }
+ "Freek" should "work" in {
+ import cats._
+ import cats.free.Free
+ import cats.implicits._
+
+ import freek._
+
+ object Test {
+ sealed trait Instruction[T]
+ // Seq[Int] doesn't represent and error but is the return type of Get
+ final case class Get() extends Instruction[List[Int]]
+
+ type PRG = Instruction :|: NilDSL
+ val PRG = freek.DSL.Make[PRG]
+ type O = Option :&: List :&: Bulb
+
+ Get().freek[PRG].onionT[O]
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/scala/LongCompileSpec.scala b/src/test/scala/LongCompileSpec.scala
index 647ba9f..5a101fd 100644
--- a/src/test/scala/LongCompileSpec.scala
+++ b/src/test/scala/LongCompileSpec.scala
@@ -7,7 +7,6 @@ package freek
import org.scalatest._
import cats.free.{Free, Trampoline}
-import cats.data.Xor
import cats.{~>, Id}
import scala.concurrent._
@@ -18,6 +17,7 @@ import cats.Functor
import cats.instances.future._
import cats.instances.option._
import cats.instances.list._
+import cats.instances.either._
import ExecutionContext.Implicits.global
import freek._