Repository: djspiewak/emm Branch: master Commit: 5ca2a814d3a3 Files: 37 Total size: 73.7 KB Directory structure: gitextract_z29rar3e/ ├── .gitignore ├── .travis.yml ├── README.md ├── build.sbt ├── cats/ │ ├── build.sbt │ └── src/ │ ├── main/ │ │ └── scala/ │ │ └── emm/ │ │ └── compat/ │ │ └── cats.scala │ └── test/ │ └── scala/ │ └── emm/ │ ├── ApplicativeSpecs.scala │ ├── BinderSpecs.scala │ ├── ExpanderCollapserSpecs.scala │ ├── FutureSpecs.scala │ ├── LifterSpecs.scala │ ├── MapperSpecs.scala │ ├── TestHelpers.scala │ └── WrapperSpecs.scala ├── core/ │ ├── build.sbt │ └── src/ │ └── main/ │ └── scala/ │ └── emm/ │ ├── Emm.scala │ ├── effects/ │ │ ├── Binder.scala │ │ ├── Collapser.scala │ │ ├── Expander.scala │ │ ├── Lifter.scala │ │ ├── Mapper.scala │ │ ├── MapperBinder.scala │ │ ├── Traverser.scala │ │ └── Wrapper.scala │ ├── effects.scala │ ├── package.scala │ ├── permute.scala │ └── properties/ │ ├── NestedAtPoint.scala │ ├── NonNested.scala │ └── extract.scala ├── project/ │ ├── Build.scala │ ├── build.properties │ └── plugins.sbt ├── scalaz/ │ └── src/ │ ├── main/ │ │ └── scala/ │ │ └── emm/ │ │ └── compat/ │ │ └── scalaz.scala │ └── test/ │ └── scala/ │ └── emm/ │ └── ScalazSpecs.scala ├── scalaz71/ │ └── build.sbt └── scalaz72/ └── build.sbt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ target/ # vim *.sw? # Ignore [ce]tags files tags ================================================ FILE: .travis.yml ================================================ language: scala scala: - 2.11.7 jdk: - oraclejdk7 - oraclejdk8 ================================================ FILE: README.md ================================================ # Generalized Effect Composition Otherwise known as "less confusing monad transformers". **Warning** The monad produced by `Emm` is *not* guaranteed to be a lawful monad! In fact, it isn't even guaranteed to be a lawful applicative. You can see an example of a violation of the applicative laws [here.](https://gitter.im/typelevel/cats?at=5763685752352c840283176a) (much thanks to @TomasMikula!) I'm leaving this repository up for pedagogical reasons; it's still an interesting exploration of Scala's type system. But I do *not* recommend you use it for real work, given that the monad is not lawful. --- The `Emm` monad provides a syntactically lightweight, type-inference friendly data type for composing effects. The general motivation is very similar to monad transformers, but the end result is far more user friendly and also significantly more general. The main goals of the project are as follows: - Simple and easy to understand - Clean type inference - Clean type *errors* (dear god, monad transformer compile errors...) - Compatibility with pre-existing monads These goals are very similar to those which motivated [Oleg's `Eff`](http://okmij.org/ftp/Haskell/extensible/), which is a really terrific data structure. There are some significant differences though. Most notably, `Eff` requires effect implementations to be rewritten to be compatible with its internal calculus, and so it does not allow the composition of arbitrary "standalone" monads written in a conventional style. However, `Eff` is able to provide much greater expressive power than `Emm` (or monad transformers) in several key diminsions. Oleg goes into significant detail on the expressiveness gains of `Eff` in his paper describing the construct. `Emm` does not provide the same benefits. ## SBT Setup If you want to use `Emm` in your project, adding the following SBT configuration will do the trick: ```sbt libraryDependencies += "com.codecommit" %% "emm-core" % EmmVersion ``` You will also need to bring in the appropriate upstream framework support for either Scalaz or Cats, depending on which one you're using. ```sbt libraryDependencies += "com.codecommit" %% "emm-scalaz-71" % EmmVersion // for scalaz 7.1 // or! libraryDependencies += "com.codecommit" %% "emm-scalaz-72" % EmmVersion // for scalaz 7.2 // or! libraryDependencies += "com.codecommit" %% "emm-cats" % EmmVersion // for cats 0.4.1 ``` You will want to use either `emm-scalaz` or `emm-cats`. While there is no technical reason you would not be able to use both in the same project, doing so would be… weird. At present, Cats support is slightly more complete than Scalaz, but we aim to reach parity soon. The most recent stable version of Emm is **0.2.1**. ```sbt val EmmVersion = "0.2.1" ``` Snapshot builds are often published as versions derived from the git hash. For example, `0.2-a21c63a`. The version prefix indicates *compatibility* with a particular version line, not derivation or antecedence. Not all git hashes are published, but some are. When in doubt, try a few. Or just ask for one to be published. All artifacts are signed with public key fingerprint [2BAE 5960](https://keybase.io/djspiewak). ## Example ```scala import emm._ import emm.compat.scalaz._ import scalaz.concurrent.Task import scalaz.std.option._ def readName: Task[String] = ??? def log(msg: String): Task[Unit] = ??? type E = Task |: Option |: Base val effect: Emm[E, String] = for { first <- readName.liftM[E] last <- readName.liftM[E] name <- (if ((first.length * last.length) < 20) Some(s"$first $last") else None).liftM[E] _ <- log(s"successfully read in $name").liftM[E] } yield name ``` The above is analogous to monad transformers in many ways. In fact, we can write the exact same code from above using `OptionT`: ```scala import scalaz._ import scalaz.concurrent.Task import scalaz.syntax.monad._ def readName: Task[String] = ??? def log(msg: String): Task[Unit] = ??? val effect: OptionT[Task, String] = for { first <- readName.liftM[OptionT] last <- readName.liftM[OptionT] name <- (if ((first.length * last.length) < 20) OptionT.some[Task, String](s"$first $last") else OptionT.none[Task, String]) _ <- log(s"successfully read in $name").liftM[OptionT] } yield name ``` The advantages of `Emm` become much more apparent when attempting to stack more than just two monads simultaneously. For example, one might imagine stacking `Task`, `Option` and right-biased `Either`. Let's enrich our previous example with some error handling (note that I'm using [kind projector](https://github.com/non/kind-projector) to avoid type lambdas): ```scala import emm._ import emm.compat.scalaz._ import scalaz._ import scalaz.concurrent.Task import scalaz.std.option._ def readName: Task[String] = ??? def log(msg: String): Task[Unit] = ??? type E = Task |: (String \/ ?) |: Option |: Base val effect: Emm[E, String] = for { first <- readName.liftM[E] last <- readName.liftM[E] name <- (if ((first.length * last.length) < 20) Some(s"$first $last") else None).liftM[E] _ <- (if (name == "Daniel Spiewak") -\/("your kind isn't welcome here") else \/-(())).liftM[E] _ <- log(s"successfully read in $name").liftM[E] } yield name ``` It works as expected, with all the same syntax as before. However, if we look at the same example using monad transformers, a rather distopian picture emerges: ```scala import scalaz._ import scalaz.concurrent.Task import scalaz.syntax.monad._ def readName: Task[String] = ??? def log(msg: String): Task[Unit] = ??? val effect: OptionT[EitherT[Task, String, ?], String] = for { first <- readName.liftM[EitherT[?[_], String, ?]].liftM[OptionT] last <- readName.liftM[(EitherT[?[_], String, ?]].liftM[OptionT] name <- if ((first.length * last.length) < 20) OptionT.some[EitherT[Task, String, ?], String](s"$first $last") else OptionT.none[EitherT[Task, String, ?], String] _ <- (if (name == "Daniel Spiewak") EitherT.fromDisjunction[Task](\/.left[String, Unit]("your kind isn't welcome here")) else EitherT.fromDisjunction[Task](\/.right[String, Unit](()))).liftM[OptionT] _ <- log(s"successfully read in $name").liftM[EitherT[?[_], String, ?]].liftM[OptionT] } yield name ``` That's a *lot* of very explicit lifting and special syntax. I had to ponder quite long and hard about the above, and I'm not even sure if I got it all right! Monad transformers are very ugly, very cumbersome, and when you get things wrong they explode in remarkably spectacular ways. The `Emm` monad is intended to change all of that. It is intended to be very straightforward to manage and extend complex stacks of effects, and to do so without any special wrappers or added complexity from the effect author. No need to write an `OptionT`, just use `Option`! ## API The following API is provided. For starters, the following pair of functions are implicitly provided to lift values into the effect stack: - `pointM[C <: Effects]` – Points a value of type `A` into the monad, `Emm[C, A]`. Requires an `Applicative` for each component of the effect stack `C`. - `liftM[C <: Effects]` – Given an effect which is of a type contained within `C`, lift the effect into the full effect stack represented by `C`. For example: `Option(42).liftM[Task |: Option |: Base]` - `wrapM[C <: Effects]` – Given a full stack of effects which matches the stack `C`, wrap the stack in the `Emm` monad. Note that the `C` parameter can be inferred basically 100% of the time, but can be provided explicitly to assert correctness. Example: `(Task now Option(42)).wrapM`. This is equivalent to calling the `Emm(...)` constructor, but the type inference is much nicer. These methods are exposed via implicit classes contained within the `emm` package object. All of the above methods are aliased on the `Emm` object as `point`, `lift` and `wrap`, respectively. You'll notice, however, that they do require a bit of extra type annotation since the target type and the effect stack are in the same type block, rather than separate ones (as in the case of implicitly provided members). Thus, you should generally prefer the "`M` versions" of each method wherever possible (i.e. when not importing `scalaz.syntax.monad._`). The `Emm` monad itself provides the following (effective) API: - `map[B](A => B): Emm[C, B]` – Conventional functor map. Transforms the value within the effect - `flatMap[B](A => Emm[C, B]): Emm[C, B]` – Monadic bind. Transforms the value within the effect and joins the two effect stacks. This function requires that all components of `C` define a `bind` function, and all components aside from the outer-most (left-most) must have a `Traverse` instance. - `flatMapM[G[_], B](A => G[B]): Emm[C, B]` – Similar to `flatMap`, except instead of transforming the value to an effect contained within the entire effect stack, `C`, it transforms the value to a single component of that effect stack. Thus, `G` must be in `C`. The result is joined with the effect stack and returned within `Emm`. - `expand` – The inverse of `collapse`. Converts an `Emm` of the form `Emm[... |: F |: Base, A]` into `Emm[... |: Base, F[A]]`. This is extremely useful when there are effect-specific functions (e.g. `Option#getOrElse`) that you need to access on the inner-most (right-most) effect of the stack. Once you have expanded, you can use `map` or `flatMap` to access these functions and manipulate the inner-most effect. Runs in constant time. - `collapse` – The inverse of `expand`. Converts an `Emm` of the form `Emm[... |: Base, F[A]]` into `Emm[... |: F |: Base, A]`. This is generally most useful in conjunction with `expand`, where you have manipulated the inner-most effect and you need to "recombine" the results of that manipulation with the full effect stack. Runs in constant time. - `run` – Unwraps the effect stack (without modification) from `Emm`. Effectively, this takes a type of the form `Emm[F |: G |: Base, A]` and produces a type of the form `F[G[A]]`. Literally, it is the "contents" of `Emm`. ## Requirements Right now, this is sitting on top of the [shims](https://github.com/djspiewak/shims) 0.2 typeclass hierarchy, which is to say that it supports Cats 0.3, Scalaz 7.2 and 7.1. Everything is implemented in terms of the following type classes (with minimal constraints for every function): - `Applicative` - `FlatMap` - `Functor` - `Traverse` ### Invalid and Partially-Valid Stacks Constraints which are not required to evaluate a given function are not assumed. For example, consider the following effect stack: ```scala type E = Option |: Task |: Base val effect = Option(42).liftM[E] ``` If you attempt to run `flatMap` on this effect stack, you will run into problems: ```scala effect flatMapM { i => if (i < 20) None else Some(i * 2) } // does not compile! ``` This will fail because `Task` is *not* the outer-most effect, which is to say, it isn't the effect on the far left of the effect definition. The reason this is a problem becomes more clear if we look at things in terms of `map`, `flatten` and the raw stack, rather than simply `flatMap` and the collapsed `Emm` monad: ```scala val effect2: Option[Task[Int]] = Some(Task now 42) val mapped: Option[Task[Option[Task[Int]]]] = effect2 map { t => t map { i => if (i < 20) None else Some(Task now (i * 2)) } } val result: Option[Task[Int]] = mapped.flatten // ?????? ``` Notice the problem here. We need to take the second `Option` layer, which is *within* a `Task`, and "flip" it outside of the `Task` layer in order to flatten the `Option` and `Task` layers together. Basically, we want to do something like this: ```scala Option[Task[Option[Task[Int]]]] => Option[Option[Task[Task[Int]]]] => Option[Task[Task[Int]]] => Option[Task[Int]] ``` Clearly, there are no problems with the last two stages, but that second stage is completely impossible. We can't take a value from inside `Task` and "flip" it to the outside. `Task` is basically a `Future`, so the value "inside" of `Task` doesn't even exist yet! So this effect stack is non-sensical as a monad; we cannot define `flatMap` (or equivalently, `flatMapM`) on it, and the compiler is very happy to tell us so. Technically, the reason we *can't* do this is because there is no instance `Traverse[Task]`, and in fact you cannot define such an instance without actually running the `Task`. Our example from earlier though, where our stack was `Task |: Option |: Base` was just fine, because there *is* an instance `Traverse[Option]`. Here's the cool bit though. Even though it doesn't make any sense to define `flatMap` on `Emm[Option |: Task |: Base, Int]`, there's no reason why we can't define `map`! ```scala type E = Option |: Task |: Base val effect = Option(42).liftM[E] val effect2 = effect map { _ * 2 } // no problemo! ``` Even though our effect stack is sort of bogus, it's only bogus if we attempt to treat it *as a monad*. It's a perfectly valid applicative functor, and we can treat it as such. In other words, `flatMap` doesn't work (and shouldn't work!) on some effect stacks, but `map` works on any effect stack where each component effect has a `Functor`. ## Limitations Maybe this section should be nearer to the top... Oh well. The most significant limitation of this approach is caused by everyone's favorite limitation of the scalac type checker, [SI-2712](https://issues.scala-lang.org/browse/SI-2712). The good news is that this bug is not a complete show stopper; it's relatively easy to work around when you control the entire stack of type signatures (as I do here) and you're not trying to generalize over different type constructor arities. The bad news is that it makes my life very difficult, and it imposes some pretty hard limits (also related to how much boilerplate I'm willing to type out) on what sorts of type constructors do and do not work with `Emm`. Specifically, the following *kinds* of type constructors are accepted (i.e. will be fully functional in any position of an effect stack): - `* -> *` – Examples: `Option`, `List`, `Task` - `* x * -> *` – Examples: `Either`, `State` (with caveats), `Writer` (more caveats), `Reader` (sorry, still caveated) - `* x * x * -> *` – Examples: *No idea* - `(* -> *) x * -> *` – Examples: `Free`, `OptionT`, `ListT`, `StreamT` - `(* -> *) x * x * -> *` – Examples: *uh...* - `(* -> *) x * x * x * -> *` – Examples: `IndexedStateT` (sort of) ================================================ FILE: build.sbt ================================================ lazy val commonSettings = Seq( organization := "com.codecommit", licenses += ("Apache-2.0", url("http://www.apache.org/licenses/")), scalaVersion := "2.11.7", crossScalaVersions := Seq(scalaVersion.value), shimsVersion := "0.3", libraryDependencies += "org.specs2" %% "specs2-core" % "3.7" % "test", addCompilerPlugin("org.spire-math" % "kind-projector" % "0.7.1" cross CrossVersion.binary), scalacOptions += "-language:_", // I really can't be bothered with SIP-18 scalacOptions += "-Ybackend:GenBCode", // scalacOptions += "-Xlog-implicits", scalacOptions in Test += "-Yrangepos", isSnapshot := version.value endsWith "SNAPSHOT", // so… sonatype doesn't like git hash snapshots publishMavenStyle := true, pomIncludeRepository := { _ => false }, sonatypeProfileName := "com.codecommit", pomExtra := djspiewak Daniel Spiewak http://www.codecommit.com alissapajer Alissa Pajer , homepage := Some(url("https://github.com/djspiewak/emm")), scmInfo := Some(ScmInfo(url("https://github.com/djspiewak/emm"), "git@github.com:djspiewak/emm.git"))) lazy val root = project.in(file(".")).settings(commonSettings: _*).aggregate(core, cats, scalaz71, scalaz72).settings( name := "emm", publish := (), publishLocal := (), publishArtifact := false) lazy val core = project.in(file("core")).settings(commonSettings: _*) lazy val cats = project.in(file("cats")).settings(commonSettings: _*).dependsOn(core) lazy val scalaz72 = project.in(file("scalaz72")).settings(commonSettings: _*).dependsOn(core) lazy val scalaz71 = project.in(file("scalaz71")).settings(commonSettings: _*).dependsOn(core) enablePlugins(GitVersioning) val ReleaseTag = """^v([\d\.]+)$""".r git.baseVersion := "0.2" git.gitTagToVersionNumber := { case ReleaseTag(version) => Some(version) case _ => None } git.formattedShaVersion := { val suffix = git.makeUncommittedSignifierSuffix(git.gitUncommittedChanges.value, git.uncommittedSignifier.value) git.gitHeadCommit.value map { _.substring(0, 7) } map { sha => git.baseVersion.value + "-" + sha + suffix } } git.gitUncommittedChanges := "git status -s".!!.trim.length > 0 ================================================ FILE: cats/build.sbt ================================================ name := "emm-cats" libraryDependencies ++= Seq( "com.codecommit" %% "shims-cats" % shimsVersion.value, "org.scalaz" %% "scalaz-core" % "7.1.6" % "test") ================================================ FILE: cats/src/main/scala/emm/compat/cats.scala ================================================ package emm package compat import effects._ import properties._ import _root_.cats.~> import _root_.cats.data.Kleisli private[emm] object Shared { implicit def functor[F <: Effects](implicit F: Mapper[F]): _root_.cats.Functor[F#Point] = new _root_.cats.Functor[F#Point] { def map[A, B](fa: F#Point[A])(f: A => B): F#Point[B] = F.map(fa)(f) } implicit def monad[F <: Effects](implicit FM: Mapper[F], FB: Binder[F]): _root_.cats.Monad[F#Point] = new _root_.cats.Monad[F#Point] { def pure[A](a: A) = FM.point(a) override def map[A, B](fa: F#Point[A])(f: A => B): F#Point[B] = FM.map(fa)(f) def flatMap[A, B](fa: F#Point[A])(f: A => F#Point[B]): F#Point[B] = FB.bind(fa)(f) } } private[emm] trait BinderShims { implicit def pivotKleisliBinder[Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Kleisli[?[_], Z, ?], F, T], FB: Binder[F], FM: Mapper[F], TB: Binder[T], TT: Traverser[T]): Binder[C] = new Binder[C] { import Shared.monad def bind[A, B](fca: CC[A])(f: A => CC[B]): CC[B] = { val back = NAP.unpack(fca) flatMap { ca => val ptta = TT.traverse[Kleisli[F#Point, Z, ?], A, T#Point[B]](ca)({ a => NAP.unpack(f(a)) })(shims.cats.applicative1[Kleisli[F#Point, Z, ?]](Kleisli.kleisliApplicative[F#Point, Z])) ptta map { tta => TB.bind(tta) { a => a } } } NAP.pack(back) } } } private[emm] trait LifterShims { implicit def midKleisli[A, Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Kleisli[?[_], Z, ?], F, T], F: Mapper[F], T: Mapper[T]): Lifter.Aux[Kleisli[λ[X => X], Z, A], C, A] = new Lifter[Kleisli[λ[X => X], Z, A], C] { type Out = A import Shared.functor def apply(e: Kleisli[λ[X => X], Z, A]): CC[Out] = { val t = new (λ[X => X] ~> F#Point) { def apply[A](a: A): F#Point[A] = F.point(a) } NAP.pack(e.transform[F#Point](t) map T.point) } } implicit def leftPivotKleisli[E, Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Kleisli[?[_], Z, ?], F, T], T: Mapper[T], F: Lifter[E, F], FM: Mapper[F]): Lifter.Aux[E, C, F.Out] = new Lifter[E, C] { type Out = F.Out def apply(e: E): CC[Out] = NAP.pack(Kleisli[F#Point, Z, T#Point[F.Out]]({ _ => FM.map(F(e)) { a => T.point(a) } })) } implicit def rightPivotKleisli[E, Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Kleisli[?[_], Z, ?], F, T], T: Lifter[E, T], F: Mapper[F]): Lifter.Aux[E, C, T.Out] = new Lifter[E, C] { type Out = T.Out def apply(e: E): CC[Out] = NAP.pack(Kleisli[F#Point, Z, T#Point[T.Out]] { _ => F.point(T(e)) }) } } private[emm] trait MapperShims { // we require a Binder[F] because we cannot derive a consistent Applicative from Mapper[F] implicit def pivotKleisliMapper[Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Kleisli[?[_], Z, ?], F, T], FB: Binder[F], FM: Mapper[F], T: Mapper[T]): Mapper[C] = new Mapper[C] { import Shared.monad def point[A](a: A): CC[A] = NAP.pack(Kleisli.pure[F#Point, Z, T#Point[A]](T.point(a))) def map[A, B](fa: CC[A])(f: A => B): CC[B] = NAP.pack(NAP.unpack(fa) map { ta => T.map(ta)(f) }) } } object cats extends shims.Implicits with BinderShims with LifterShims with MapperShims ================================================ FILE: cats/src/test/scala/emm/ApplicativeSpecs.scala ================================================ package emm import org.specs2.mutable._ import cats._ import cats.std.list._ import cats.std.option._ /*object ApplicativeSpecs extends Specification { "derived applicative" should { "be consistent with bind in Option |: List" in { type E = Option |: List |: Base val a = Emm[E, Int](None) val b = Emm[E, Int => Int](Some(Nil)) // swap the following two blocks and the test will fail // val A = Emm.applicativeInstance[E] // val M = Emm.monadInstance[E] val A = Applicative[Emm[E, ?]] val M = Monad[Emm[E, ?]] A.ap(a)(b) mustEqual (M.flatMap(b) { b => M.map(a)(b) }) M.ap(a)(b) mustEqual (M.flatMap(b) { b => M.map(a)(b) }) } } }*/ ================================================ FILE: cats/src/test/scala/emm/BinderSpecs.scala ================================================ package emm import emm.compat.cats._ import org.specs2.mutable._ import cats._ import cats.data._ import cats.free.Free import cats.std.list._ import cats.std.option._ import cats.std.function._ import scalaz.concurrent.Task object BinderSpecs extends Specification with TestHelpers { "simple effect composition with binder" should { "allow binding" in { type E = List |: Option |: Base val e = for { v <- List(1, 2, 3, 4).liftM[E] v2 <- (Some(v) filter { _ % 2 == 0 }).liftM[E] } yield v2 e mustEqual Emm[E, Int](List(None, Some(2), None, Some(4))) } "allow binding over a Option of Kleisli of List" in { type E = Option |: Kleisli[?[_], Int, ?] -|: List |: Base import effects._ import properties._ "foobar".pointM[E].flatMap(x => (x + "baz").pointM[E]).run.run(42) mustEqual Some(List("foobarbaz")) } "allow binding over a Option of List of Kleisli" in { type E = Option |: List |: Kleisli[?[_], Int, ?] -|: Base "foobar".pointM[E].flatMap(x => (x + "baz").pointM[E]).run.run(42) mustEqual Some(List("foobarbaz")) } "bind over a stack that contains a partially-applied arity-2 constructor" in { type E = (String Xor ?) |: Base 42.pointM[E] flatMap { _ => "foo".pointM[E] } mustEqual Emm[E, String](Xor.right("foo")) } "bind over a stack that contains a partially-applied arity-2 higher-order constructor" in { "base" >> { type E = Free[List, ?] |: Base (42.pointM[E] flatMap { _ => "foo".pointM[E] } run).runM(identity) mustEqual List("foo") } "inner" >> { type E = Option |: Free[List, ?] |: Base (42.pointM[E] flatMap { _ => "foo".pointM[E] } run) must beLike { case Some(f) => f.runM(identity) mustEqual List("foo") } } "outer" >> { type E = Free[List, ?] |: Option |: Base (42.pointM[E] flatMap { _ => "foo".pointM[E] } run).runM(identity) mustEqual List(Option("foo")) } } "bind over a stack that contains state" in { "inner" >> { type E = Option |: StateT[?[_], String, ?] -|: Base (42.pointM[E] flatMap { _ => "foo".pointM[E] }).run.runA("blah") must beSome("foo") } "mid" >> { type E = List |: StateT[?[_], String, ?] -|: Option |: Base (42.pointM[E] flatMap { _ => "foo".pointM[E] }).run.runA("blah") mustEqual List(Some("foo")) } } "enable flatMapM in any direction" in { type E = List |: Option |: Base val e1 = List(1, 2, 3, 4).liftM[E] val e2 = e1 flatMapM { v => Some(v) filter { _ % 2 == 0 } } val e3 = e2 flatMapM { v => List(v, v) } e3 mustEqual Emm[E, Int](List(None, Some(2), Some(2), None, Some(4), Some(4))) } "allow flatMapM on a stack containing an arity-2 constructor" in { type E = List |: (String Xor ?) |: Base val e1 = List(1, 2, 3, 4).liftM[E] val e2 = e1 flatMapM { v => if (v % 2 == 0) Xor.right(v) else Xor.left("that's... odd") } val e3 = e2 flatMapM { v => List(v, v) } e3 mustEqual Emm[E, Int](List(Xor.left("that's... odd"), Xor.right(2), Xor.right(2), Xor.left("that's... odd"), Xor.right(4), Xor.right(4))) } "allow flatMapM on a stack containing a higher-order arity-2 constructor" in { type E = List |: Free[Option, ?] |: Base val e1 = List(1, 2, 3, 4).liftM[E] val e2 = e1 flatMapM { v => if (v % 2 == 0) Free.pure[Option, Int](v) else Free.liftF[Option, Int](None) } val e3 = e2 flatMapM { v => List(v, v) } e3.run must beLike { case List(f1, f2, f3, f4, f5, f6) => { f1.runM(identity) mustEqual None f2.runM(identity) mustEqual Some(2) f3.runM(identity) mustEqual Some(2) f4.runM(identity) mustEqual None f5.runM(identity) mustEqual Some(4) f6.runM(identity) mustEqual Some(4) } } } } "non-traversable effect composition with binder" should { "allow binding where the non-traversable effect is outermost" in { type E = Task |: Option |: Base val opt: Option[Int] = Some(42) var sink = 0 val e = for { i <- opt.liftM[E] _ <- (Task delay { sink += i }).liftM[E] } yield () e.run.run sink mustEqual 42 } } } ================================================ FILE: cats/src/test/scala/emm/ExpanderCollapserSpecs.scala ================================================ package emm import emm.compat.cats._ import org.specs2.mutable._ import cats._ import cats.data._ import cats.free.Free import cats.std.list._ import cats.std.option._ import cats.std.function._ import scalaz.concurrent.Task import scalaz.\/- object ExpanderCollapserSpecs extends Specification with TestHelpers { "basic simple effect composition" should { "define pointM" in { 42.pointM[Option |: List |: Base] mustEqual Emm[Option |: List |: Base, Int](Option(List(42))) } } "non-traversable effect composition with expander and collapser" should { "enable access to the base of the stack" in { type E = Task |: Option |: Base val opt: Option[Int] = None // this type infers better than a single function val e = opt.liftM[E].expand map { _ getOrElse 12 } e.run.run mustEqual 12 } "allow both expansion and collapse of base" in { type E = Task |: Option |: Base val opt: Option[Int] = None // this type infers better than a single function val e = opt.liftM[E].expand map { _ orElse Some(24) } collapse e.run.run must beSome(24) } "allow both expansion and collapse of base with an arity-2 constructor" in { "inner" >> { type E = Task |: (String Xor ?) |: Base val e = (Task now 42).liftM[E].expand map { _.swap } collapse e.run.run mustEqual Xor.left(42) } "outer" >> { type E = (String Xor ?) |: Task |: Base val e = (Task now 42).liftM[E].expand map { _.attempt } collapse e.run must beLike { case Xor.Right(t) => t.run mustEqual \/-(42) } } } "allow both expansion and collapse of base with a higher-order arity-2 constructor" in { val toList = new (Option ~> List) { def apply[A](xs: Option[A]) = xs.toList } "inner" >> { type E = Task |: Free[Option, ?] |: Base val e = (Task now 42).liftM[E].expand map { _ mapSuspension toList } collapse e.run.run.runM(identity) mustEqual List(42) } "outer" >> { type E = Free[Option, ?] |: Task |: Base val e = (Task now 42).liftM[E].expand map { _.attempt } collapse e.run.runM(identity) must beLike { case Some(t) => t.run mustEqual \/-(42) } } } } } ================================================ FILE: cats/src/test/scala/emm/FutureSpecs.scala ================================================ package emm import emm.compat.cats._ import org.specs2.mutable._ import cats.std.list._ import cats.std.option._ import cats.std.future._ import scala.concurrent.{Await, Future} import scala.concurrent.duration._ object FutureSpecs extends Specification { implicit val ec = scala.concurrent.ExecutionContext.global "emm with future" should { "compose with list and option" in { type E = Future |: Option |: List |: Base val bam = for { f <- Future(3).liftM[E] o <- Option(2).liftM[E] l <- List(1, 1).liftM[E] } yield f + o + l Await.result(bam.run, 20 seconds) must beSome(List(6, 6)) } } } ================================================ FILE: cats/src/test/scala/emm/LifterSpecs.scala ================================================ package emm import emm.compat.cats._ import org.specs2.mutable._ import cats._ import cats.data._ import cats.std.list._ import cats.std.option._ import cats.free.Free import scalaz.concurrent.Task object LifterSpecs extends Specification with TestHelpers { "simple effect composition with lifter" should { "allow lifting in either direction" in { val opt: Option[Int] = Some(42) opt.liftM[Option |: List |: Base] must haveType[Emm[Option |: List |: Base, Int]].attempt opt.liftM[List |: Option |: Base] must haveType[Emm[List |: Option |: Base, Int]].attempt } "lift into an effect stack of depth three" in { type E = Task |: List |: Option |: Base Option(42).liftM[E].run.run mustEqual List(Option(42)) List(42).liftM[E].run.run mustEqual List(Option(42)) (Task now 42).liftM[E].run.run mustEqual List(Option(42)) } "lift into a stack that contains a partially-applied arity-2 constructor" in { "inner" >> { type E = Option |: (String Xor ?) |: Base Xor.right[String, Int](42).liftM[E] mustEqual Emm[E, Int](Option(Xor.right(42))) Xor.left[String, Int]("fuuuuuu").liftM[E] mustEqual Emm[E, Int](Option(Xor.left("fuuuuuu"))) } "outer" >> { type E = (String Xor ?) |: Option |: Base Xor.right[String, Int](42).liftM[E] mustEqual Emm[E, Int](Xor.right(Option(42))) Xor.left[String, Int]("fuuuuuu").liftM[E] mustEqual Emm[E, Int](Xor.left("fuuuuuu")) } } "lift into a stack that contains a partially-applied arity-2 higher-order constructor" in { "inner" >> { type E = Option |: Free[List, ?] |: Base Free.pure[List, Int](42).liftM[E].run must beLike { case Some(f) => f runM identity mustEqual List(42) } Option(42).liftM[E].run must beLike { case Some(f) => f runM identity mustEqual List(42) } } "outer" >> { type E = Free[List, ?] |: Option |: Base Free.pure[List, Int](42).liftM[E].run.runM(identity) mustEqual List(Option(42)) Option(42).liftM[E].run.runM(identity) mustEqual List(Option(42)) } } "lift into a stack that contains a partially-applied arity-2 higher-order constructor and an arity-2 constructor" in { "inner" >> { type E = (String Xor ?) |: Free[List, ?] |: Base Free.pure[List, Int](42).liftM[E].run must beLike { case Xor.Right(f) => f runM identity mustEqual List(42) } Xor.right[String, Int](42).liftM[E].run must beLike { case Xor.Right(f) => f runM identity mustEqual List(42) } } "outer" >> { type E = Free[List, ?] |: (String Xor ?) |: Base Free.pure[List, Int](42).liftM[E].run.runM(identity) mustEqual List(Xor.right(42)) Xor.right[String, Int](42).liftM[E].run.runM(identity) mustEqual List(Xor.right(42)) } } "lift into a stack that contains a kleisli" in { import cats.data.Kleisli "inner" >> { type E = Option |: Kleisli[?[_], String, ?] -|: Base Kleisli.pure[λ[X => X], String, Int](12).liftM[E].run.run("foo") must beSome(12) Option(42).liftM[E].run.run("foo") must beSome(42) } "outer" >> { type E = Free[List, ?] |: Option |: Base Free.pure[List, Int](42).liftM[E].run.runM(identity) mustEqual List(Option(42)) Option(42).liftM[E].run.runM(identity) mustEqual List(Option(42)) } } } } ================================================ FILE: cats/src/test/scala/emm/MapperSpecs.scala ================================================ package emm import emm.compat.cats._ import org.specs2.mutable._ import cats._ import cats.data._ import cats.std.list._ import cats.std.option._ import scalaz.concurrent.Task object MapperSpecs extends Specification with TestHelpers { "simple effect composition with mapper" should { "allow mapping" in { val opt: Option[Int] = Some(42) val e = opt.liftM[List |: Option |: Base] e map (2 *) mustEqual Emm[List |: Option |: Base, Int](List(Some(84))) } "allow mapping over a Kleisli" in { type E = Option |: Kleisli[?[_], Int, ?] -|: Base "foobar".pointM[E].map(_ + "baz").run.run(42) must beSome("foobarbaz") } "allow mapping over a List of Option of Kleisli" in { type E = List |: Option |: Kleisli[?[_], Int, ?] -|: Base "foobar".pointM[E].map(_ + "baz").run.run(42) mustEqual List(Some("foobarbaz")) } "allow mapping over a Option of Kleisli of List" in { type E = Option |: Kleisli[?[_], Int, ?] -|: List |: Base "foobar".pointM[E].map(_ + "baz").run.run(42) mustEqual Some(List("foobarbaz")) } } "non-traversable effect composition with mapper" should { "allow mapping in either direction" in { val opt: Option[Int] = Some(42) opt.liftM[Task |: Option |: Base] map (2 *) opt.liftM[Option |: Task |: Base] map (2 *) ok } } } ================================================ FILE: cats/src/test/scala/emm/TestHelpers.scala ================================================ package emm import org.specs2.matcher._ import cats._ import cats.data._ import cats.free.Free import scalaz.concurrent.Task import scala.reflect.runtime.universe.TypeTag trait TestHelpers { def haveType[A](implicit A: TypeTag[A]) = new { def attempt[B](implicit B: TypeTag[B]): Matcher[B] = new Matcher[B] { def apply[B2 <: B](s: Expectable[B2]) = result(A.tpe =:= B.tpe, s"${s.description} has type ${A.tpe}", s"${s.description} does not have type ${A.tpe}; has type ${B.tpe}", s) } } implicit val taskFlatMap: Monad[Task] = new Monad[Task] { import scalaz.concurrent.Future def pure[A](x: A): Task[A] = new Task(Future.delay(Task.Try(x))) def flatMap[A, B](fa: Task[A])(f: A => Task[B]): Task[B] = { fa.flatMap(f) } } implicit def freeTraverse[F[_]](implicit trF: Traverse[F]): Traverse[Free[F, ?]] = new Traverse[Free[F, ?]] { def traverse[G[_], A, B](fa: Free[F, A])(f: A => G[B])(implicit ap: Applicative[G]): G[Free[F, B]] = fa.resume match { case Xor.Left(s) => ap.map(trF.traverse(s)(fa => traverse[G, A ,B](fa)(f)))(ffa => Free.liftF(ffa).flatMap(a => a)) case Xor.Right(a) => ap.map(f(a))(Free.pure(_)) } def foldLeft[A, B](fa: Free[F, A], b: B)(f: (B, A) => B): B = ??? def foldRight[A, B](fa: Free[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = ??? } } ================================================ FILE: cats/src/test/scala/emm/WrapperSpecs.scala ================================================ package emm import emm.compat.cats._ import org.specs2.mutable._ import cats._ import cats.data._ import cats.free.Free import cats.std.option._ import cats.std.list._ object WrapperSpecs extends Specification with TestHelpers { "simple effect composition with wrapper" should { "allow wrapping of two paired constructors" in { Option(List(42)).wrapM must haveType[Emm[Option |: List |: Base, Int]].attempt Option(List(42)).wrapM[Option |: List |: Base] must haveType[Emm[Option |: List |: Base, Int]].attempt } "allow wrapping of two paired constructors where one has arity-2" in { "inner" >> { type E = Option |: (String Xor ?) |: Base Option(Xor.right[String, Int](42)).wrapM must haveType[Emm[Option |: (String Xor ?) |: Base, Int]].attempt Option(Xor.right[String, Int](42)).wrapM[E] must haveType[Emm[Option |: (String Xor ?) |: Base, Int]].attempt } "outer" >> { type E = (String Xor ?) |: Option |: Base Xor.right[String, Option[Int]](Option(42)).wrapM must haveType[Emm[(String Xor ?) |: Option |: Base, Int]].attempt Xor.right[String, Option[Int]](Option(42)).wrapM[E] must haveType[Emm[(String Xor ?) |: Option |: Base, Int]].attempt } } "allow wrapping of two paired constructors where one is higher-order and has arity-2" in { "inner" >> { type E = Option |: Free[List, ?] |: Base Option(Free.pure[List, Int](42)).wrapM must haveType[Emm[Option |: Free[List, ?] |: Base, Int]].attempt Option(Free.pure[List, Int](42)).wrapM[E] must haveType[Emm[Option |: Free[List, ?] |: Base, Int]].attempt } "outer" >> { type E = Free[List, ?] |: Option |: Base Free.pure[List, Option[Int]](Option(42)).wrapM must haveType[Emm[Free[List, ?] |: Option |: Base, Int]].attempt Free.pure[List, Option[Int]](Option(42)).wrapM[E] must haveType[Emm[Free[List, ?] |: Option |: Base, Int]].attempt } } } } ================================================ FILE: core/build.sbt ================================================ name := "emm-core" libraryDependencies += "com.codecommit" %% "shims-core" % shimsVersion.value ================================================ FILE: core/src/main/scala/emm/Emm.scala ================================================ package emm final case class Emm[C <: Effects, A](run: C#Point[A]) { import effects._ def map[B](f: A => B)(implicit C: Mapper[C]): Emm[C, B] = Emm(C.map(run)(f)) def flatMap[B](f: A => Emm[C, B])(implicit B: Binder[C]): Emm[C, B] = Emm(B.bind(run) { a => f(a).run }) def flatMapM[E](f: A => E)(implicit E: Lifter[E, C], B: Binder[C]): Emm[C, E.Out] = flatMap { a => Emm(E(f(a))) } def expand(implicit C: Expander[C]): Emm[C.Out, C.CC[A]] = Emm(C(run)) def collapse(implicit C: Collapser[A, C]): Emm[C.Out, C.A] = Emm(C(run)) } object Emm { import effects._ def point[C <: Effects, A](a: A)(implicit M: Mapper[C]): Emm[C, A] = Emm[C, A](M.point(a)) def lift[C <: Effects, A](a: A)(implicit L: Lifter[A, C]): Emm[C, L.Out] = Emm(L(a)) def wrap[C <: Effects, A](a: A)(implicit W: Wrapper[A, C]): Emm[C, W.A] = Emm[C, W.A](W(a)) } /*trait EmmLowPriorityImplicits1 { import effects._ implicit def functorInstance[C <: Effects](implicit C: Mapper[C]): Functor[Emm[C, ?]] = new Functor[Emm[C, ?]] { def map[A, B](fa: Emm[C, A])(f: A => B): Emm[C, B] = new Emm(C.map(fa.run)(f)) } } trait EmmLowPriorityImplicits2 extends EmmLowPriorityImplicits1 { import effects._ implicit def monadInstance[C <: Effects : Mapper : Binder]: Monad[Emm[C, ?]] = new Monad[Emm[C, ?]] { def pure[A](a: A): Emm[C, A] = new Emm(implicitly[Mapper[C]].point(a)) def flatMap[A, B](fa: Emm[C, A])(f: A => Emm[C, B]): Emm[C, B] = fa flatMap f } } object Emm extends EmmLowPriorityImplicits2 { import effects._ implicit def traverseInstance[C <: Effects](implicit C: Traverser[C]): Traverse[Emm[C, ?]] = new Traverse[Emm[C, ?]] { def traverse[G[_]: Applicative, A, B](fa: Emm[C, A])(f: A => G[B]): G[Emm[C, B]] = Applicative[G].map(C.traverse(fa.run)(f)) { new Emm(_) } def foldLeft[A, B](fa: Emm[C, A], b: B)(f: (B, A) => B): B = C.foldLeft(fa.run, b)(f) def foldRight[A, B](fa: Emm[C, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = C.foldRight(fa.run, lb)(f) } }*/ ================================================ FILE: core/src/main/scala/emm/effects/Binder.scala ================================================ package emm package effects import shims.{Applicative, FlatMap, Functor, Monad, Traverse} import scala.annotation.implicitNotFound import properties._ @implicitNotFound("could not prove ${C} is a valid monadic stack; perhaps an effect is lacking a FlatMap, or a non-outer effect is lacking a Traverse") trait Binder[C <: Effects] { type CC[A] = C#Point[A] def bind[A, B](cca: CC[A])(f: A => CC[B]): CC[B] } object Binder { implicit def base: Binder[Base] = new Binder[Base] { def bind[A, B](fa: A)(f: A => B): B = f(fa) } implicit def corecurse1[F[_], C <: Effects](implicit C: Binder[C], T: Traverser[C], NN: NonNested[C], F: Applicative[F], B: FlatMap[F]): Binder[F |: C] = new Binder[F |: C] { def bind[A, B](fca: CC[A])(f: A => CC[B]): CC[B] = { val back = B.flatMap(NN.unpack(fca)) { ca => val fcaca = T.traverse(ca) { a => NN.unpack(f(a)) } F.map(fcaca) { caca => C.bind(caca) { a => a } } } NN.pack(back) } } implicit def corecurse2[F[_, _], F2[_, _], Z, C <: Effects](implicit ev: Permute2[F, F2], C: Binder[C], NN: NonNested[C], T: Traverser[C], F: Applicative[F2[Z, ?]], B: FlatMap[F2[Z, ?]]): Binder[F2[Z, ?] |: C] = corecurse1[F2[Z, ?], C] implicit def corecurse3[F[_, _, _], F2[_, _, _], Y, Z, C <: Effects](implicit ev: Permute3[F, F2], C: Binder[C], NN: NonNested[C], T: Traverser[C], F: Applicative[F2[Y, Z, ?]], B: FlatMap[F2[Y, Z, ?]]): Binder[F2[Y, Z, ?] |: C] = corecurse1[F2[Y, Z, ?], C] implicit def corecurseH1[F[_[_], _], G[_], C <: Effects](implicit C: Binder[C], T: Traverser[C], NN: NonNested[C], F: Applicative[F[G, ?]], B: FlatMap[F[G, ?]]): Binder[F[G, ?] |: C] = corecurse1[F[G, ?], C] implicit def corecurseH2[F[_[_], _, _], F2[_[_], _, _], G[_], Z, C <: Effects](implicit ev: PermuteH2[F, F2], C: Binder[C], T: Traverser[C], NN: NonNested[C], F: Applicative[F2[G, Z, ?]], B: FlatMap[F2[G, Z, ?]]): Binder[F2[G, Z, ?] |: C] = corecurse1[F2[G, Z, ?], C] implicit def corecurseH3[F[_[_], _, _, _], F2[_[_], _, _, _], G[_], Y, Z, C <: Effects](implicit ev: PermuteH3[F, F2], C: Binder[C], T: Traverser[C], NN: NonNested[C], F: Applicative[F2[G, Y, Z, ?]], B: FlatMap[F2[G, Y, Z, ?]]): Binder[F2[G, Y, Z, ?] |: C] = corecurse1[F2[G, Y, Z, ?], C] implicit def pivot1[Pivot[_[_], _], C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot, F, T], Pivot: Monad[Pivot[F#Point, ?]], TB: Binder[T], TT: Traverser[T]): Binder[C] = new Binder[C] { def bind[A, B](fca: CC[A])(f: A => CC[B]): CC[B] = { val back: Pivot[F#Point, T#Point[B]] = Pivot.flatMap(NAP.unpack(fca)) { ca => val ptta: Pivot[F#Point, T#Point[T#Point[B]]] = TT.traverse[Pivot[F#Point, ?], A, T#Point[B]](ca)(a => NAP.unpack(f(a))) Pivot.map(ptta)(tta => TB.bind(tta)(a => a)) } NAP.pack(back) } } implicit def pivot2[Pivot[_[_], _, _], Pivot2[_[_], _, _], Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot2[?[_], Z, ?], F, T], ev: PermuteH2[Pivot, Pivot2], Pivot: Monad[Pivot2[F#Point, Z, ?]], TB: Binder[T], TT: Traverser[T]): Binder[C] = pivot1[Pivot2[?[_], Z, ?], C, F, T] implicit def pivot3[Pivot[_[_], _, _, _], Pivot2[_[_], _, _, _], Y, Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot2[?[_], Y, Z, ?], F, T], ev: PermuteH3[Pivot, Pivot2], Pivot: Monad[Pivot2[F#Point, Y, Z, ?]], TB: Binder[T], TT: Traverser[T]): Binder[C] = pivot1[Pivot2[?[_], Y, Z, ?], C, F, T] } ================================================ FILE: core/src/main/scala/emm/effects/Collapser.scala ================================================ package emm package effects import shims.{Applicative, FlatMap, Functor, Monad, Traverse} import scala.annotation.implicitNotFound import properties._ trait Collapser[E, C <: Effects] { type A type Out <: Effects type Point[A] = C#Point[A] def apply(fa: Point[E]): Out#Point[A] } trait CollapserLowPriorityImplicits2 { def head1[F[_], A0]: Collapser.Aux[F[A0], Base, A0, F |: Base] implicit def headH2[F[_[_], _, _], F2[_[_], _, _], G[_], Z, A0](implicit ev: PermuteH2[F, F2]): Collapser.Aux[F2[G, Z, A0], Base, A0, F2[G, Z, ?] |: Base] = head1[F2[G, Z, ?], A0] implicit def headH3[F[_[_], _, _, _], F2[_[_], _, _, _], G[_], Y, Z, A0](implicit ev: PermuteH3[F, F2]): Collapser.Aux[F2[G, Y, Z, A0], Base, A0, F2[G, Y, Z, ?] |: Base] = head1[F2[G, Y, Z, ?], A0] def corecurse1[E, F[_], C <: Effects](implicit C: Collapser[E, C]): Collapser.Aux[E, F |: C, C.A, F |: C.Out] implicit def corecurseH2[E, F[_[_], _, _], F2[_[_], _, _], G[_], Z, C <: Effects](implicit ev: PermuteH2[F, F2], C: Collapser[E, C]): Collapser.Aux[E, F2[G, Z, ?] |: C, C.A, F2[G, Z, ?] |: C.Out] = corecurse1[E, F2[G, Z, ?], C] implicit def corecurseH3[E, F[_[_], _, _, _], F2[_[_], _, _, _], G[_], Y, Z, C <: Effects](implicit ev: PermuteH3[F, F2], C: Collapser[E, C]): Collapser.Aux[E, F2[G, Y, Z, ?] |: C, C.A, F2[G, Y, Z, ?] |: C.Out] = corecurse1[E, F2[G, Y, Z, ?], C] } object Collapser extends CollapserLowPriorityImplicits2 { type Aux[E, C <: Effects, A0, Out0 <: Effects] = Collapser[E, C] { type A = A0; type Out = Out0 } implicit def head1[F[_], A0]: Collapser.Aux[F[A0], Base, A0, F |: Base] = new Collapser[F[A0], Base] { type A = A0 type Out = F |: Base def apply(fa: F[A]): F[A] = fa } implicit def head2[F[_, _], F2[_, _], Z, A0](implicit ev: Permute2[F, F2]): Collapser.Aux[F2[Z, A0], Base, A0, F2[Z, ?] |: Base] = head1[F2[Z, ?], A0] implicit def head3[F[_, _, _], F2[_, _, _], Y, Z, A0](implicit ev: Permute3[F, F2]): Collapser.Aux[F2[Y, Z, A0], Base, A0, F2[Y, Z, ?] |: Base] = head1[F2[Y, Z, ?], A0] implicit def headH1[F[_[_], _], G[_], A0]: Collapser.Aux[F[G, A0], Base, A0, F[G, ?] |: Base] = head1[F[G, ?], A0] implicit def corecurse1[E, F[_], C <: Effects](implicit C: Collapser[E, C]): Collapser.Aux[E, F |: C, C.A, F |: C.Out] = new Collapser[E, F |: C] { type A = C.A type Out = F |: C.Out def apply(gca: Point[E]): Out#Point[A] = gca.asInstanceOf[Out#Point[A]] // already proven equivalent; evaluation requires a Functor } implicit def corecurse2[E, F[_, _], F2[_, _], Z, C <: Effects](implicit ev: Permute2[F, F2], C: Collapser[E, C]): Collapser.Aux[E, F2[Z, ?] |: C, C.A, F2[Z, ?] |: C.Out] = corecurse1[E, F2[Z, ?], C] implicit def corecurse3[E, F[_, _, _], F2[_, _, _], Y, Z, C <: Effects](implicit ev: Permute3[F, F2], C: Collapser[E, C]): Collapser.Aux[E, F2[Y, Z, ?] |: C, C.A, F2[Y, Z, ?] |: C.Out] = corecurse1[E, F2[Y, Z, ?], C] implicit def corecurseH1[E, F[_[_], _], G[_], C <: Effects](implicit C: Collapser[E, C]): Collapser.Aux[E, F[G, ?] |: C, C.A, F[G, ?] |: C.Out] = corecurse1[E, F[G, ?], C] // C == F ++ (Pivot -|: T) implicit def pivot1[E, Pivot[_[_], _], C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot, F, T], T: Collapser[E, T]): Collapser.Aux[E, C, T.A, F#Append[Pivot -|: T.Out]] = new Collapser[E, C] { type A = T.A type Out = F#Append[Pivot -|: T.Out] def apply(gca: Point[E]): Out#Point[A] = gca.asInstanceOf[Out#Point[A]] // already proven equivalent; evaluation requires a Functor } implicit def pivot2[E, Pivot[_[_], _, _], Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot[?[_], Z, ?], F, T], T: Collapser[E, T]): Collapser.Aux[E, C, T.A, F#Append[Pivot[?[_], Z, ?] -|: T.Out]] = pivot1[E, Pivot[?[_], Z, ?], C, F, T] implicit def pivot3[E, Pivot[_[_], _, _, _], Y, Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot[?[_], Y, Z, ?], F, T], T: Collapser[E, T]): Collapser.Aux[E, C, T.A, F#Append[Pivot[?[_], Y, Z, ?] -|: T.Out]] = pivot1[E, Pivot[?[_], Y, Z, ?], C, F, T] } ================================================ FILE: core/src/main/scala/emm/effects/Expander.scala ================================================ package emm package effects import shims.{Applicative, FlatMap, Functor, Monad, Traverse} import scala.annotation.implicitNotFound import properties._ trait Expander[C <: Effects] { type CC[_] type Out <: Effects type Point[A] = C#Point[A] def apply[A](fa: Point[A]): Out#Point[CC[A]] } object Expander { type Aux[C <: Effects, CC0[_], Out0 <: Effects] = Expander[C] { type CC[A] = CC0[A]; type Out = Out0 } implicit def head1[F[_]]: Expander.Aux[F |: Base, F, Base] = new Expander[F |: Base] { type CC[A] = F[A] type Out = Base def apply[A](fa: F[A]): F[A] = fa } implicit def head2[F[_, _], F2[_, _], Z](implicit ev: Permute2[F, F2]): Expander.Aux[F2[Z, ?] |: Base, F2[Z, ?], Base] = head1[F2[Z, ?]] implicit def head3[F[_, _, _], F2[_, _, _], Y, Z](implicit ev: Permute3[F, F2]): Expander.Aux[F2[Y, Z, ?] |: Base, F2[Y, Z, ?], Base] = head1[F2[Y, Z, ?]] implicit def headH1[F[_[_], _], G[_]]: Expander.Aux[F[G, ?] |: Base, F[G, ?], Base] = head1[F[G, ?]] implicit def headH2[F[_[_], _, _], F2[_[_], _, _], G[_], Z](implicit ev: PermuteH2[F, F2]): Expander.Aux[F2[G, Z, ?] |: Base, F2[G, Z, ?], Base] = head1[F2[G, Z, ?]] implicit def headH3[F[_[_], _, _, _], F2[_[_], _, _, _], G[_], Y, Z](implicit ev: PermuteH3[F, F2]): Expander.Aux[F2[G, Y, Z, ?] |: Base, F2[G, Y, Z, ?], Base] = head1[F2[G, Y, Z, ?]] implicit def corecurse1[F[_], C <: Effects](implicit C: Expander[C]): Expander.Aux[F |: C, C.CC, F |: C.Out] = new Expander[F |: C] { type CC[A] = C.CC[A] type Out = F |: C.Out def apply[A](gca: Point[A]): Out#Point[CC[A]] = gca.asInstanceOf[Out#Point[CC[A]]] // already proven equivalent; evaluation requires a Functor } implicit def corecurse2[F[_, _], F2[_, _], Z, C <: Effects](implicit ev: Permute2[F, F2], C: Expander[C]): Expander.Aux[F2[Z, ?] |: C, C.CC, F2[Z, ?] |: C.Out] = corecurse1[F2[Z, ?], C] implicit def corecurse3[F[_, _, _], F2[_, _, _], Y, Z, C <: Effects](implicit ev: Permute3[F, F2], C: Expander[C]): Expander.Aux[F2[Y, Z, ?] |: C, C.CC, F2[Y, Z, ?] |: C.Out] = corecurse1[F2[Y, Z, ?], C] implicit def corecurseH1[F[_[_], _], G[_], C <: Effects](implicit C: Expander[C]): Expander.Aux[F[G, ?] |: C, C.CC, F[G, ?] |: C.Out] = corecurse1[F[G, ?], C] implicit def corecurseH2[F[_[_], _, _], F2[_[_], _, _], G[_], Z, C <: Effects](implicit ev: PermuteH2[F, F2], C: Expander[C]): Expander.Aux[F2[G, Z, ?] |: C, C.CC, F2[G, Z, ?] |: C.Out] = corecurse1[F2[G, Z, ?], C] implicit def corecurseH3[F[_[_], _, _, _], F2[_[_], _, _, _], G[_], Y, Z, C <: Effects](implicit ev: PermuteH3[F, F2], C: Expander[C]): Expander.Aux[F2[G, Y, Z, ?] |: C, C.CC, F2[G, Y, Z, ?] |: C.Out] = corecurse1[F2[G, Y, Z, ?], C] implicit def pivot1Base[Pivot[_[_], _], C <: Effects, F <: Effects](implicit NAP: NestedAtPoint[C, Pivot, F, Base]): Expander.Aux[C, Pivot[F#Point, ?], Base] = new Expander[C] { type CC[A] = Pivot[F#Point, A] type Out = Base def apply[A](gca: C#Point[A]): Out#Point[CC[A]] = gca.asInstanceOf[Out#Point[CC[A]]] // already proven equivalent; evaluation requires a Functor } implicit def pivot2Base[Pivot[_[_], _, _], Z, C <: Effects, F <: Effects](implicit NAP: NestedAtPoint[C, Pivot[?[_], Z, ?], F, Base]): Expander.Aux[C, Pivot[F#Point, Z, ?], Base] = pivot1Base[Pivot[?[_], Z, ?], C, F](NAP) implicit def pivot3Base[Pivot[_[_], _, _, _], Y, Z, C <: Effects, F <: Effects](implicit NAP: NestedAtPoint[C, Pivot[?[_], Y, Z, ?], F, Base]): Expander.Aux[C, Pivot[F#Point, Y, Z, ?], Base] = pivot1Base[Pivot[?[_], Y, Z, ?], C, F](NAP) // C == F ++ (Pivot -|: T) implicit def pivot1[Pivot[_[_], _], C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot, F, T], T: Expander[T]): Expander.Aux[C, T.CC, F#Append[Pivot -|: T.Out]] = new Expander[C] { type CC[A] = T.CC[A] type Out = F#Append[Pivot -|: T.Out] def apply[A](gca: C#Point[A]): Out#Point[CC[A]] = gca.asInstanceOf[Out#Point[CC[A]]] // already proven equivalent; evaluation requires a Functor } implicit def pivot2[Pivot[_[_], _, _], Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot[?[_], Z, ?], F, T], T: Expander[T]): Expander.Aux[C, T.CC, F#Append[Pivot[?[_], Z, ?] -|: T.Out]] = pivot1[Pivot[?[_], Z, ?], C, F, T] implicit def pivot3[Pivot[_[_], _, _, _], Y, Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot[?[_], Y, Z, ?], F, T], T: Expander[T]): Expander.Aux[C, T.CC, F#Append[Pivot[?[_], Y, Z, ?] -|: T.Out]] = pivot1[Pivot[?[_], Y, Z, ?], C, F, T] } ================================================ FILE: core/src/main/scala/emm/effects/Lifter.scala ================================================ package emm package effects import shims.{Applicative, FlatMap, Functor, Monad, Traverse} import scala.annotation.implicitNotFound import properties._ @implicitNotFound("could not lift ${E} into stack ${C}; either ${C} does not contain a constructor of ${E}, or there is no Functor for a constructor of ${E}") trait Lifter[E, C <: Effects] { type Out type CC[A] = C#Point[A] def apply(e: E): CC[Out] } object Lifter { type Aux[E, C <: Effects, Out0] = Lifter[E, C] { type Out = Out0 } implicit def mid1[F[_], A, C <: Effects](implicit C: Mapper[C], F: Functor[F], NN: NonNested[C]): Lifter.Aux[F[A], F |: C, A] = new Lifter[F[A], F |: C] { type Out = A def apply(fa: F[A]) = NN.pack(F.map(fa) { a => C.point(a) }) } implicit def mid2[F[_, _], F2[_, _], Z, A, C <: Effects](implicit ev: Permute2[F, F2], C: Mapper[C], F: Functor[F2[Z, ?]], NN: NonNested[C]): Lifter.Aux[F2[Z, A], F2[Z, ?] |: C, A] = mid1[F2[Z, ?], A, C] implicit def mid3[F[_, _, _], F2[_, _, _], Y, Z, A, C <: Effects](implicit ev: Permute3[F, F2], C: Mapper[C], F: Functor[F2[Y, Z, ?]], NN: NonNested[C]): Lifter.Aux[F2[Y, Z, A], F2[Y, Z, ?] |: C, A] = mid1[F2[Y, Z, ?], A, C] implicit def midH1[F[_[_], _], G[_], A, C <: Effects](implicit C: Mapper[C], F: Functor[F[G, ?]], NN: NonNested[C]): Lifter.Aux[F[G, A], F[G, ?] |: C, A] = mid1[F[G, ?], A, C] implicit def midH2[F[_[_], _, _], F2[_[_], _, _], G[_], Z, A, C <: Effects](implicit ev: PermuteH2[F, F2], C: Mapper[C], F: Functor[F2[G, Z, ?]], NN: NonNested[C]): Lifter.Aux[F2[G, Z, A], F2[G, Z, ?] |: C, A] = mid1[F2[G, Z, ?], A, C] implicit def midH3[F[_[_], _, _, _], F2[_[_], _, _, _], G[_], Y, Z, A, C <: Effects](implicit ev: PermuteH3[F, F2], C: Mapper[C], F: Functor[F2[G, Y, Z, ?]], NN: NonNested[C]): Lifter.Aux[F2[G, Y, Z, A], F2[G, Y, Z, ?] |: C, A] = mid1[F2[G, Y, Z, ?], A, C] implicit def corecurse1[F[_], E, C <: Effects](implicit L: Lifter[E, C], F: Applicative[F], NN: NonNested[C]): Lifter.Aux[E, F |: C, L.Out] = new Lifter[E, F |: C] { type Out = L.Out def apply(e: E) = NN.pack(F.point(L(e))) } implicit def corecurse2[F[_, _], F2[_, _], Z, E, C <: Effects](implicit ev: Permute2[F, F2], L: Lifter[E, C], F: Applicative[F2[Z, ?]], NN: NonNested[C]): Lifter.Aux[E, F2[Z, ?] |: C, L.Out] = corecurse1[F2[Z, ?], E, C] implicit def corecurse3[F[_, _, _], F2[_, _, _], Y, Z, E, C <: Effects](implicit ev: Permute3[F, F2], L: Lifter[E, C], F: Applicative[F2[Y, Z, ?]], NN: NonNested[C]): Lifter.Aux[E, F2[Y, Z, ?] |: C, L.Out] = corecurse1[F2[Y, Z, ?], E, C] implicit def corecurseH1[F[_[_], _], G[_], E, C <: Effects](implicit L: Lifter[E, C], F: Applicative[F[G, ?]], NN: NonNested[C]): Lifter.Aux[E, F[G, ?] |: C, L.Out] = corecurse1[F[G, ?], E, C] implicit def corecurseH2[F[_[_], _, _], F2[_[_], _, _], G[_], Z, E, C <: Effects](implicit ev: PermuteH2[F, F2], L: Lifter[E, C], F: Applicative[F2[G, Z, ?]], NN: NonNested[C]): Lifter.Aux[E, F2[G, Z, ?] |: C, L.Out] = corecurse1[F2[G, Z, ?], E, C] implicit def corecurseH3[F[_[_], _, _, _], F2[_[_], _, _, _], G[_], Y, Z, E, C <: Effects](implicit ev: PermuteH3[F, F2], L: Lifter[E, C], F: Applicative[F2[G, Y, Z, ?]], NN: NonNested[C]): Lifter.Aux[E, F2[G, Y, Z, ?] |: C, L.Out] = corecurse1[F2[G, Y, Z, ?], E, C] } ================================================ FILE: core/src/main/scala/emm/effects/Mapper.scala ================================================ package emm package effects import shims.{Applicative, FlatMap, Functor, Monad, Traverse} import scala.annotation.implicitNotFound import properties._ @implicitNotFound("could not compute a method for mapping over effect stack ${C}; either a member of the stack lacks an Applicative, or its Applicative instance is ambiguous") trait Mapper[C <: Effects] { outer => type CC[A] = C#Point[A] def point[A](a: A): CC[A] def map[A, B](fa: CC[A])(f: A => B): CC[B] def functor: Functor[CC] = new Functor[CC] { def map[A, B](fa: CC[A])(f: A => B): CC[B] = outer.map(fa)(f) } } object Mapper { implicit def base: Mapper[Base] = new Mapper[Base] { def point[A](a: A) = a def map[A, B](fa: A)(f: A => B) = f(fa) } implicit def corecurse1[F[_], C <: Effects](implicit P: Mapper[C], NN: NonNested[C], F: Applicative[F]): Mapper[F |: C] = new Mapper[F |: C] { def point[A](a: A) = NN.pack(F.point(P.point(a))) def map[A, B](fa: CC[A])(f: A => B): CC[B] = NN.pack(F.map(NN.unpack(fa)) { ca => P.map(ca)(f) }) } implicit def corecurse2[F[_, _], F2[_, _], Z, C <: Effects](implicit ev: Permute2[F, F2], P: Mapper[C], NN: NonNested[C], F: Applicative[F2[Z, ?]]): Mapper[F2[Z, ?] |: C] = corecurse1[F2[Z, ?], C] implicit def corecurse3[F[_, _, _], F2[_, _, _], Y, Z, C <: Effects](implicit ev: Permute3[F, F2], P: Mapper[C], NN: NonNested[C], F: Applicative[F2[Y, Z, ?]]): Mapper[F2[Y, Z, ?] |: C] = corecurse1[F2[Y, Z, ?], C] implicit def corecurseH1[F[_[_], _], G[_], C <: Effects](implicit P: Mapper[C], NN: NonNested[C], F: Applicative[F[G, ?]]): Mapper[F[G, ?] |: C] = corecurse1[F[G, ?], C] implicit def corecurseH2[F[_[_], _, _], F2[_[_], _, _], G[_], Z, C <: Effects](implicit ev: PermuteH2[F, F2], P: Mapper[C], NN: NonNested[C], F: Applicative[F2[G, Z, ?]]): Mapper[F2[G, Z, ?] |: C] = corecurse1[F2[G, Z, ?], C] implicit def corecurseH3[F[_[_], _, _, _], F2[_[_], _, _, _], G[_], Y, Z, C <: Effects](implicit ev: PermuteH3[F, F2], P: Mapper[C], NN: NonNested[C], F: Applicative[F2[G, Y, Z, ?]]): Mapper[F2[G, Y, Z, ?] |: C] = corecurse1[F2[G, Y, Z, ?], C] implicit def pivot1[Pivot[_[_], _], C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot, F, T], Pivot: Applicative[Pivot[F#Point, ?]], T: Mapper[T]): Mapper[C] = new Mapper[C] { def point[A](a: A): CC[A] = NAP.pack(Pivot.point(T.point(a))) def map[A, B](fa: CC[A])(f: A => B): CC[B] = NAP.pack(Pivot.map(NAP.unpack(fa)) { ta => T.map(ta)(f) }) } implicit def pivot2[Pivot[_[_], _, _], Pivot2[_[_], _, _], Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot2[?[_], Z, ?], F, T], ev: PermuteH2[Pivot, Pivot2], Pivot: Applicative[Pivot2[F#Point, Z, ?]], T: Mapper[T]): Mapper[C] = pivot1[Pivot2[?[_], Z, ?], C, F, T] implicit def pivot3[Pivot[_[_], _, _, _], Pivot2[_[_], _, _, _], Y, Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot2[?[_], Y, Z, ?], F, T], ev: PermuteH3[Pivot, Pivot2], Pivot: Applicative[Pivot2[F#Point, Y, Z, ?]], T: Mapper[T]): Mapper[C] = pivot1[Pivot2[?[_], Y, Z, ?], C, F, T] } ================================================ FILE: core/src/main/scala/emm/effects/MapperBinder.scala ================================================ package emm package effects import shims.Monad private[emm] object MapperBinder { implicit def monad[F <: Effects](implicit FB: Binder[F], FM: Mapper[F]): Monad[F#Point] = new Monad[F#Point] { def point[A](a: A): F#Point[A] = FM.point(a) override def map[A, B](fa: F#Point[A])(f: A => B): F#Point[B] = FM.map(fa)(f) def flatMap[A, B](fa: F#Point[A])(f: A => F#Point[B]): F#Point[B] = FB.bind(fa)(f) } } ================================================ FILE: core/src/main/scala/emm/effects/Traverser.scala ================================================ package emm package effects import shims.{Applicative, FlatMap, Functor, Monad, Traverse} import scala.annotation.implicitNotFound import properties._ trait Traverser[C <: Effects] { type CC[A] = C#Point[A] def traverse[G[_]: Applicative, A, B](ca: CC[A])(f: A => G[B]): G[CC[B]] } object Traverser { implicit def base: Traverser[Base] = new Traverser[Base] { def traverse[G[_]: Applicative, A, B](fa: A)(f: A => G[B]): G[B] = f(fa) } implicit def corecurse1[F[_], C <: Effects](implicit C: Traverser[C], NN: NonNested[C], F: Traverse[F]): Traverser[F |: C] = new Traverser[F |: C] { def traverse[G[_]: Applicative, A, B](fca: CC[A])(f: A => G[B]): G[CC[B]] = { val back = F.traverse(NN.unpack(fca)) { ca => C.traverse(ca)(f) } implicitly[Applicative[G]].map(back) { fca2 => NN.pack(fca2) } } } implicit def corecurse2[F[_, _], F2[_, _], Z, C <: Effects](implicit ev: Permute2[F, F2], C: Traverser[C], NN: NonNested[C], F: Traverse[F2[Z, ?]]): Traverser[F2[Z, ?] |: C] = corecurse1[F2[Z, ?], C] implicit def corecurse3[F[_, _, _], F2[_, _, _], Y, Z, C <: Effects](implicit ev: Permute3[F, F2], C: Traverser[C], NN: NonNested[C], F: Traverse[F2[Y, Z, ?]]): Traverser[F2[Y, Z, ?] |: C] = corecurse1[F2[Y, Z, ?], C] implicit def corecurseH1[F[_[_], _], G0[_], C <: Effects](implicit C: Traverser[C], NN: NonNested[C], F: Traverse[F[G0, ?]]): Traverser[F[G0, ?] |: C] = corecurse1[F[G0, ?], C] implicit def corecurseH2[F[_[_], _, _], F2[_[_], _, _], G0[_], Z, C <: Effects](implicit ev: PermuteH2[F, F2], C: Traverser[C], NN: NonNested[C], F: Traverse[F2[G0, Z, ?]]): Traverser[F2[G0, Z, ?] |: C] = corecurse1[F2[G0, Z, ?], C] implicit def corecurseH3[F[_[_], _, _, _], F2[_[_], _, _, _], G0[_], Y, Z, C <: Effects](implicit ev: PermuteH3[F, F2], C: Traverser[C], NN: NonNested[C], F: Traverse[F2[G0, Y, Z, ?]]): Traverser[F2[G0, Y, Z, ?] |: C] = corecurse1[F2[G0, Y, Z, ?], C] implicit def pivot1[Pivot[_[_], _], C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot, F, T], T: Traverser[T], Pivot: Traverse[Pivot[F#Point, ?]]): Traverser[C] = new Traverser[C] { def traverse[G[_]: Applicative, A, B](fca: CC[A])(f: A => G[B]): G[CC[B]] = { val back = Pivot.traverse(NAP.unpack(fca)) { ca => T.traverse(ca)(f) } implicitly[Applicative[G]].map(back) { fca2 => NAP.pack(fca2) } } } implicit def pivot2[Pivot[_[_], _, _], Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot[?[_], Z, ?], F, T], T: Traverser[T], Pivot: Traverse[Pivot[F#Point, Z, ?]]): Traverser[C] = pivot1[Pivot[?[_], Z, ?], C, F, T] implicit def pivot3[Pivot[_[_], _, _, _], Y, Z, C <: Effects, F <: Effects, T <: Effects](implicit NAP: NestedAtPoint[C, Pivot[?[_], Y, Z, ?], F, T], T: Traverser[T], Pivot: Traverse[Pivot[F#Point, Y, Z, ?]]): Traverser[C] = pivot1[Pivot[?[_], Y, Z, ?], C, F, T] } ================================================ FILE: core/src/main/scala/emm/effects/Wrapper.scala ================================================ package emm package effects import shims.{Applicative, FlatMap, Functor, Monad, Traverse} import scala.annotation.implicitNotFound import properties._ @implicitNotFound("could not infer effect stack ${C} from type ${E}") trait Wrapper[E, C <: Effects] { type A type CC[A] = C#Point[A] def apply(e: E): CC[A] } trait WrapperLowPriorityImplicits1 { implicit def head[A0]: Wrapper.Aux[A0, Base, A0] = new Wrapper[A0, Base] { type A = A0 def apply(a: A) = a } } // not really sure why these functions in particular need to be moved down trait WrapperLowPriorityImplicits2 extends WrapperLowPriorityImplicits1 { def corecurse1[F[_], E, C <: Effects, A0](implicit W: Wrapper.Aux[E, C, A0]): Wrapper.Aux[F[E], F |: C, A0] implicit def corecurseH2[F[_[_], _, _], F2[_[_], _, _], G[_], Z, E, C <: Effects, A0](implicit ev: PermuteH2[F, F2], W: Wrapper.Aux[E, C, A0]): Wrapper.Aux[F2[G, Z, E], F2[G, Z, ?] |: C, A0] = corecurse1[F2[G, Z, ?], E, C, A0] implicit def corecurseH3[F[_[_], _, _, _], F2[_[_], _, _, _], G[_], Y, Z, E, C <: Effects, A0](implicit ev: PermuteH3[F, F2], W: Wrapper.Aux[E, C, A0]): Wrapper.Aux[F2[G, Y, Z, E], F2[G, Y, Z, ?] |: C, A0] = corecurse1[F2[G, Y, Z, ?], E, C, A0] } object Wrapper extends WrapperLowPriorityImplicits2 { type Aux[E, C <: Effects, A0] = Wrapper[E, C] { type A = A0 } implicit def corecurse1[F[_], E, C <: Effects, A0](implicit W: Wrapper.Aux[E, C, A0]): Wrapper.Aux[F[E], F |: C, A0] = new Wrapper[F[E], F |: C] { type A = A0 def apply(fe: F[E]): CC[A] = fe.asInstanceOf[CC[A]] // already proven equivalent; actual evaluation requires a Functor } implicit def corecurse2[F[_, _], F2[_, _], Z, E, C <: Effects, A0](implicit ev: Permute2[F, F2], W: Wrapper.Aux[E, C, A0]): Wrapper.Aux[F2[Z, E], F2[Z, ?] |: C, A0] = corecurse1[F2[Z, ?], E, C, A0] implicit def corecurse3[F[_, _, _], F2[_, _, _], Y, Z, E, C <: Effects, A0](implicit ev: Permute3[F, F2], W: Wrapper.Aux[E, C, A0]): Wrapper.Aux[F2[Y, Z, E], F2[Y, Z, ?] |: C, A0] = corecurse1[F2[Y, Z, ?], E, C, A0] implicit def corecurseH1[F[_[_], _], G[_], E, C <: Effects, A0](implicit W: Wrapper.Aux[E, C, A0]): Wrapper.Aux[F[G, E], F[G, ?] |: C, A0] = corecurse1[F[G, ?], E, C, A0] } ================================================ FILE: core/src/main/scala/emm/effects.scala ================================================ package emm // there's a bug in scalac's cyclic checking with open recursion and type constructors with arity > 1 trait Partial { type Apply[F[_]] } sealed trait Effects { type Point[A] = Build[λ[X => X], A] type Build[CC[_], A] = Inner[A]#Apply[CC] type Inner[A] <: Partial type Append[E <: Effects] <: Effects } sealed trait |:[F[_], T <: Effects] extends Effects { type Inner[A] = Partial { type Apply[CC[_]] = T#Inner[A]#Apply[λ[X => CC[F[X]]]] } type Append[E <: Effects] = F |: T#Append[E] } sealed trait -|:[F[_[_], _], T <: Effects] extends Effects { type Inner[A] = Partial { type Apply[CC[_]] = F[CC, T#Inner[A]#Apply[λ[X => X]]] } type Append[E <: Effects] = F -|: T#Append[E] } sealed trait Base extends Effects { type Inner[A] = Partial { type Apply[F[_]] = F[A] } type Append[E <: Effects] = E } ================================================ FILE: core/src/main/scala/emm/package.scala ================================================ package object emm { import effects._ implicit class Syntax[A](val a: A) extends AnyVal { def pointM[C <: Effects](implicit M: Mapper[C]): Emm[C, A] = Emm.point[C, A](a) def liftM[C <: Effects](implicit L: Lifter[A, C]): Emm[C, L.Out] = Emm.lift[C, A](a) def wrapM[C <: Effects](implicit W: Wrapper[A, C]): Emm[C, W.A] = Emm.wrap[C, A](a) } } ================================================ FILE: core/src/main/scala/emm/permute.scala ================================================ package emm sealed trait Permute2[F[_, _], G[_, _]] object Permute2 { implicit def identity[F[_, _]]: Permute2[F, F] = new Permute2[F, F] {} implicit def flip[F[_, _]]: Permute2[F, λ[(A, B) => F[B, A]]] = new Permute2[F, λ[(A, B) => F[B, A]]] {} } sealed trait Permute3[F[_, _, _], G[_, _, _]] object Permute3 { implicit def abc[F[_, _, _]]: Permute3[F, F] = new Permute3[F, F] {} implicit def acb[F[_, _, _]]: Permute3[F, λ[(A, B, C) => F[A, C, B]]] = new Permute3[F, λ[(A, B, C) => F[A, C, B]]] {} implicit def bac[F[_, _, _]]: Permute3[F, λ[(A, B, C) => F[B, A, C]]] = new Permute3[F, λ[(A, B, C) => F[B, A, C]]] {} implicit def bca[F[_, _, _]]: Permute3[F, λ[(A, B, C) => F[B, C, A]]] = new Permute3[F, λ[(A, B, C) => F[B, C, A]]] {} implicit def cab[F[_, _, _]]: Permute3[F, λ[(A, B, C) => F[C, A, B]]] = new Permute3[F, λ[(A, B, C) => F[C, A, B]]] {} implicit def cba[F[_, _, _]]: Permute3[F, λ[(A, B, C) => F[C, B, A]]] = new Permute3[F, λ[(A, B, C) => F[C, B, A]]] {} } // for reasons of sanity, we're only going to support left-biased higher-order type constructors (for now) sealed trait PermuteH2[F[_[_], _, _], G[_[_], _, _]] object PermuteH2 { implicit def identity[F[_[_], _, _]]: PermuteH2[F, F] = new PermuteH2[F, F] {} implicit def flip[F[_[_], _, _]]: PermuteH2[F, λ[(G[_], A, B) => F[G, B, A]]] = new PermuteH2[F, λ[(G[_], A, B) => F[G, B, A]]] {} } // this case gets us scalaz's State, which is all I really care about sealed trait PermuteH3[F[_[_], _, _, _], G[_[_], _, _, _]] object PermuteH3 { implicit def abc[F[_[_], _, _, _]]: PermuteH3[F, F] = new PermuteH3[F, F] {} implicit def acb[F[_[_], _, _, _]]: PermuteH3[F, λ[(G[_], A, B, C) => F[G, A, C, B]]] = new PermuteH3[F, λ[(G[_], A, B, C) => F[G, A, C, B]]] {} implicit def bac[F[_[_], _, _, _]]: PermuteH3[F, λ[(G[_], A, B, C) => F[G, B, A, C]]] = new PermuteH3[F, λ[(G[_], A, B, C) => F[G, B, A, C]]] {} implicit def bca[F[_[_], _, _, _]]: PermuteH3[F, λ[(G[_], A, B, C) => F[G, B, C, A]]] = new PermuteH3[F, λ[(G[_], A, B, C) => F[G, B, C, A]]] {} implicit def cab[F[_[_], _, _, _]]: PermuteH3[F, λ[(G[_], A, B, C) => F[G, C, A, B]]] = new PermuteH3[F, λ[(G[_], A, B, C) => F[G, C, A, B]]] {} implicit def cba[F[_[_], _, _, _]]: PermuteH3[F, λ[(G[_], A, B, C) => F[G, C, B, A]]] = new PermuteH3[F, λ[(G[_], A, B, C) => F[G, C, B, A]]] {} } ================================================ FILE: core/src/main/scala/emm/properties/NestedAtPoint.scala ================================================ package emm package properties /** * The property of Effects which contain at least one -|: case, partitioning into a front and tail, where the tail * is NonNested and the front is unconstrained. */ sealed trait NestedAtPoint[C <: Effects, Pivot[_[_], _], Front <: Effects, Tail <: Effects] { def NN: NonNested[Tail] def pack[A](cc: Pivot[Front#Point, Tail#Point[A]]): C#Point[A] def unpack[A](cc: C#Point[A]): Pivot[Front#Point, Tail#Point[A]] } object NestedAtPoint { implicit def split1[Pivot[_[_], _], C <: Effects](implicit C: NonNested[C]): NestedAtPoint[Pivot -|: C, Pivot, Base, C] = new NestedAtPoint[Pivot -|: C, Pivot, Base, C] { def NN = C def pack[A](cc: Pivot[λ[X => X], C#Point[A]]): (Pivot -|: C)#Point[A] = cc.asInstanceOf[(Pivot -|: C)#Point[A]] def unpack[A](cc: (Pivot -|: C)#Point[A]): Pivot[λ[X => X], C#Point[A]] = cc.asInstanceOf[Pivot[λ[X => X], C#Point[A]]] } implicit def split2[Pivot[_[_], _, _], Pivot2[_[_], _, _], Z, C <: Effects](implicit ev: PermuteH2[Pivot, Pivot2], C: NonNested[C]): NestedAtPoint[Pivot2[?[_], Z, ?] -|: C, Pivot2[?[_], Z, ?], Base, C] = split1[Pivot2[?[_], Z, ?], C] implicit def split3[Pivot[_[_], _, _, _], Pivot2[_[_], _, _, _], Y, Z, C <: Effects](implicit ev: PermuteH3[Pivot, Pivot2], C: NonNested[C]): NestedAtPoint[Pivot2[?[_], Y, Z, ?] -|: C, Pivot2[?[_], Y, Z, ?], Base, C] = split1[Pivot2[?[_], Y, Z, ?], C] implicit def corecurseBar1[Pivot[_[_], _], C <: Effects, C2 <: Effects, F <: Effects, T <: Effects](implicit C2: BarExtract[C, C2], C: NestedAtPoint[C, Pivot, F, T]): NestedAtPoint[C2, Pivot, C2.F |: F, T] = new NestedAtPoint[C2, Pivot, C2.F |: F, T] { def NN = C.NN def pack[A](cc: Pivot[(C2.F |: F)#Point, T#Point[A]]): C2#Point[A] = cc.asInstanceOf[C2#Point[A]] def unpack[A](cc: C2#Point[A]): Pivot[(C2.F |: F)#Point, T#Point[A]] = cc.asInstanceOf[Pivot[(C2.F |: F)#Point, T#Point[A]]] } implicit def corecurseBar2[Pivot[_[_], _, _], Z, C <: Effects, C2 <: Effects, F <: Effects, T <: Effects](implicit C2: BarExtract[C, C2], C: NestedAtPoint[C, Pivot[?[_], Z, ?], F, T]): NestedAtPoint[C2, Pivot[?[_], Z, ?], C2.F |: F, T] = corecurseBar1[Pivot[?[_], Z, ?], C, C2, F, T] implicit def corecurseBar3[Pivot[_[_], _, _, _], Y, Z, C <: Effects, C2 <: Effects, F <: Effects, T <: Effects](implicit C2: BarExtract[C, C2], C: NestedAtPoint[C, Pivot[?[_], Y, Z, ?], F, T]): NestedAtPoint[C2, Pivot[?[_], Y, Z, ?], C2.F |: F, T] = corecurseBar1[Pivot[?[_], Y, Z, ?], C, C2, F, T] // these cases are currently disabled since we have no present use for them and they slow down compilation by roughly 83% /*implicit def corecursePivot1[Pivot[_[_], _], C <: Effects, C2 <: Effects, F <: Effects, T <: Effects](implicit E: PivotExtract[C, C2], C: NestedAtPoint[C, Pivot, F, T]): NestedAtPoint[C2, Pivot, E.Pivot -|: F, T] = new NestedAtPoint[C2, Pivot, E.Pivot -|: F, T] { def NN = C.NN def pack[A](cc: Pivot[(E.Pivot -|: F)#Point, T#Point[A]]): C2#Point[A] = cc.asInstanceOf[C2#Point[A]] def unpack[A](cc: C2#Point[A]): Pivot[(E.Pivot -|: F)#Point, T#Point[A]] = cc.asInstanceOf[Pivot[(E.Pivot -|: F)#Point, T#Point[A]]] } implicit def corecursePivot2[Pivot[_[_], _, _], R, C <: Effects, C2 <: Effects, F <: Effects, T <: Effects](implicit E: PivotExtract[C, C2], C: NestedAtPoint[C, Pivot[?[_], R, ?], F, T]): NestedAtPoint[C2, Pivot[?[_], R, ?], E.Pivot -|: F, T] = new NestedAtPoint[C2, Pivot[?[_], R, ?], E.Pivot -|: F, T] { def NN = C.NN def pack[A](cc: Pivot[(E.Pivot -|: F)#Point, R, T#Point[A]]): C2#Point[A] = cc.asInstanceOf[C2#Point[A]] def unpack[A](cc: C2#Point[A]): Pivot[(E.Pivot -|: F)#Point, R, T#Point[A]] = cc.asInstanceOf[Pivot[(E.Pivot -|: F)#Point, R, T#Point[A]]] } implicit def corecursePivot3[Pivot[_[_], _, _, _], Q, R, C <: Effects, C2 <: Effects, F <: Effects, T <: Effects](implicit E: PivotExtract[C, C2], C: NestedAtPoint[C, Pivot[?[_], Q, R, ?], F, T]): NestedAtPoint[C2, Pivot[?[_], Q, R, ?], E.Pivot -|: F, T] = new NestedAtPoint[C2, Pivot[?[_], Q, R, ?], E.Pivot -|: F, T] { def NN = C.NN def pack[A](cc: Pivot[(E.Pivot -|: F)#Point, Q, R, T#Point[A]]): C2#Point[A] = cc.asInstanceOf[C2#Point[A]] def unpack[A](cc: C2#Point[A]): Pivot[(E.Pivot -|: F)#Point, Q, R, T#Point[A]] = cc.asInstanceOf[Pivot[(E.Pivot -|: F)#Point, Q, R, T#Point[A]]] }*/ } ================================================ FILE: core/src/main/scala/emm/properties/NonNested.scala ================================================ package emm package properties /** * The property of Effects which do not contain a -|: case */ sealed trait NonNested[C <: Effects] { def pack[CC[_], A](cc: CC[C#Point[A]]): C#Build[CC, A] def unpack[CC[_], A](cc: C#Build[CC, A]): CC[C#Point[A]] } object NonNested { implicit def base: NonNested[Base] = new NonNested[Base] { def pack[CC[_], A](cc: CC[A]) = cc def unpack[CC[_], A](cc: CC[A]) = cc } implicit def corecurse[C <: Effects, C2 <: Effects](implicit C2: BarExtract[C, C2], C: NonNested[C]): NonNested[C2] = new NonNested[C2] { def pack[CC[_], A](cc: CC[C2#Point[A]]) = cc.asInstanceOf[C2#Build[CC, A]] def unpack[CC[_], A](cc: C2#Build[CC, A]) = cc.asInstanceOf[CC[C2#Point[A]]] } } ================================================ FILE: core/src/main/scala/emm/properties/extract.scala ================================================ package emm package properties sealed trait BarExtract[T <: Effects, C <: Effects] { type F[_] } object BarExtract { type Aux[T <: Effects, C <: Effects, F0[_]] = BarExtract[T, C] { type F[A] = F0[A] } implicit def extract1[F0[_], T <: Effects]: BarExtract.Aux[T, F0 |: T, F0] = new BarExtract[T, F0 |: T] { type F[A] = F0[A] } implicit def extract2[F0[_, _], F02[_, _], Z, T <: Effects](implicit ev: Permute2[F0, F02]): BarExtract.Aux[T, F02[Z, ?] |: T, F02[Z, ?]] = extract1[F02[Z, ?], T] implicit def extract3[F0[_, _, _], F02[_, _, _], Y, Z, T <: Effects](implicit ev: Permute3[F0, F02]): BarExtract.Aux[T, F02[Y, Z, ?] |: T, F02[Y, Z, ?]] = extract1[F02[Y, Z, ?], T] implicit def extractH1[F0[_[_], _], G[_], T <: Effects]: BarExtract.Aux[T, F0[G, ?] |: T, F0[G, ?]] = extract1[F0[G, ?], T] implicit def extractH2[F0[_[_], _, _], F02[_[_], _, _], G[_], Z, T <: Effects](implicit ev: PermuteH2[F0, F02]): BarExtract.Aux[T, F02[G, Z, ?] |: T, F02[G, Z, ?]] = extract1[F02[G, Z, ?], T] implicit def extractH3[F0[_[_], _, _, _], F02[_[_], _, _, _], G[_], Y, Z, T <: Effects](implicit ev: PermuteH3[F0, F02]): BarExtract.Aux[T, F02[G, Y, Z, ?] |: T, F02[G, Y, Z, ?]] = extract1[F02[G, Y, Z, ?], T] } sealed trait PivotExtract[T <: Effects, C <: Effects] { type Pivot[_[_], _] } object PivotExtract { type Aux[T <: Effects, C <: Effects, Pivot0[_[_], _]] = PivotExtract[T, C] { type Pivot[F[_], A] = Pivot0[F, A] } implicit def extract1[Pivot0[_[_], _], T <: Effects]: PivotExtract.Aux[T, Pivot0 -|: T, Pivot0] = new PivotExtract[T, Pivot0 -|: T] { type Pivot[F[_], A] = Pivot0[F, A] } implicit def extract2[Pivot0[_[_], _, _], Z, T <: Effects]: PivotExtract.Aux[T, Pivot0[?[_], Z, ?] -|: T, Pivot0[?[_], Z, ?]] = extract1[Pivot0[?[_], Z, ?], T] implicit def extract3[Pivot0[_[_], _, _, _], Y, Z, T <: Effects]: PivotExtract.Aux[T, Pivot0[?[_], Y, Z, ?] -|: T, Pivot0[?[_], Y, Z, ?]] = extract1[Pivot0[?[_], Y, Z, ?], T] } ================================================ FILE: project/Build.scala ================================================ import sbt._ object EmmBuild extends Build { val shimsVersion = settingKey[String]("Shims version shared between projects") } ================================================ FILE: project/build.properties ================================================ sbt.version=0.13.9 ================================================ FILE: project/plugins.sbt ================================================ addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.5") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "0.5.0") ================================================ FILE: scalaz/src/main/scala/emm/compat/scalaz.scala ================================================ package emm package compat object scalaz extends shims.Implicits ================================================ FILE: scalaz/src/test/scala/emm/ScalazSpecs.scala ================================================ package emm import emm.compat.scalaz._ import org.specs2.mutable._ import scalaz._ import scalaz.std.option._ import scalaz.std.list._ object ScalazSpecs extends Specification { "state stacks" should { "implement bind" in { "inner" >> { type E = Option |: StateT[?[_], String, ?] -|: Base (42.pointM[E] flatMap { _ => "foo".pointM[E] }).run.eval("blah") must beSome("foo") } "mid" >> { type E = List |: StateT[?[_], String, ?] -|: Option |: Base (42.pointM[E] flatMap { _ => "foo".pointM[E] }).run.eval("blah") mustEqual List(Some("foo")) } } } "kleisli stacks" should { "implement bind" in { "inner" >> { type E = Option |: Kleisli[?[_], String, ?] -|: Base (42.pointM[E] flatMap { _ => "foo".pointM[E] }).run.run("blah") must beSome("foo") } "mid" >> { type E = List |: Kleisli[?[_], String, ?] -|: Option |: Base (42.pointM[E] flatMap { _ => "foo".pointM[E] }).run.run("blah") mustEqual List(Some("foo")) } } } } ================================================ FILE: scalaz71/build.sbt ================================================ name := "emm-scalaz-71" libraryDependencies += "com.codecommit" %% "shims-scalaz-71" % shimsVersion.value ================================================ FILE: scalaz72/build.sbt ================================================ name := "emm-scalaz-72" libraryDependencies += "com.codecommit" %% "shims-scalaz-72" % shimsVersion.value