├── .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.scala │ ├── effects │ ├── Binder.scala │ ├── Collapser.scala │ ├── Expander.scala │ ├── Lifter.scala │ ├── Mapper.scala │ ├── MapperBinder.scala │ ├── Traverser.scala │ └── Wrapper.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 └── src └── scalaz72 ├── build.sbt └── src /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | 3 | # vim 4 | *.sw? 5 | 6 | # Ignore [ce]tags files 7 | tags 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | scala: 3 | - 2.11.7 4 | jdk: 5 | - oraclejdk7 6 | - oraclejdk8 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Generalized Effect Composition 2 | 3 | Otherwise known as "less confusing monad transformers". 4 | 5 | **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. 6 | 7 | --- 8 | 9 | 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: 10 | 11 | - Simple and easy to understand 12 | - Clean type inference 13 | - Clean type *errors* (dear god, monad transformer compile errors...) 14 | - Compatibility with pre-existing monads 15 | 16 | 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. 17 | 18 | ## SBT Setup 19 | 20 | If you want to use `Emm` in your project, adding the following SBT configuration will do the trick: 21 | 22 | ```sbt 23 | libraryDependencies += "com.codecommit" %% "emm-core" % EmmVersion 24 | ``` 25 | 26 | You will also need to bring in the appropriate upstream framework support for either Scalaz or Cats, depending on which one you're using. 27 | 28 | ```sbt 29 | libraryDependencies += "com.codecommit" %% "emm-scalaz-71" % EmmVersion // for scalaz 7.1 30 | 31 | // or! 32 | 33 | libraryDependencies += "com.codecommit" %% "emm-scalaz-72" % EmmVersion // for scalaz 7.2 34 | 35 | // or! 36 | 37 | libraryDependencies += "com.codecommit" %% "emm-cats" % EmmVersion // for cats 0.4.1 38 | ``` 39 | 40 | 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. 41 | 42 | The most recent stable version of Emm is **0.2.1**. 43 | 44 | ```sbt 45 | val EmmVersion = "0.2.1" 46 | ``` 47 | 48 | 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). 49 | 50 | ## Example 51 | 52 | ```scala 53 | import emm._ 54 | import emm.compat.scalaz._ 55 | 56 | import scalaz.concurrent.Task 57 | import scalaz.std.option._ 58 | 59 | def readName: Task[String] = ??? 60 | def log(msg: String): Task[Unit] = ??? 61 | 62 | type E = Task |: Option |: Base 63 | 64 | val effect: Emm[E, String] = for { 65 | first <- readName.liftM[E] 66 | last <- readName.liftM[E] 67 | 68 | name <- (if ((first.length * last.length) < 20) Some(s"$first $last") else None).liftM[E] 69 | 70 | _ <- log(s"successfully read in $name").liftM[E] 71 | } yield name 72 | ``` 73 | 74 | The above is analogous to monad transformers in many ways. In fact, we can write the exact same code from above using `OptionT`: 75 | 76 | ```scala 77 | import scalaz._ 78 | import scalaz.concurrent.Task 79 | import scalaz.syntax.monad._ 80 | 81 | def readName: Task[String] = ??? 82 | def log(msg: String): Task[Unit] = ??? 83 | 84 | val effect: OptionT[Task, String] = for { 85 | first <- readName.liftM[OptionT] 86 | last <- readName.liftM[OptionT] 87 | 88 | name <- (if ((first.length * last.length) < 20) OptionT.some[Task, String](s"$first $last") else OptionT.none[Task, String]) 89 | 90 | _ <- log(s"successfully read in $name").liftM[OptionT] 91 | } yield name 92 | ``` 93 | 94 | 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): 95 | 96 | ```scala 97 | import emm._ 98 | import emm.compat.scalaz._ 99 | 100 | import scalaz._ 101 | import scalaz.concurrent.Task 102 | import scalaz.std.option._ 103 | 104 | def readName: Task[String] = ??? 105 | def log(msg: String): Task[Unit] = ??? 106 | 107 | type E = Task |: (String \/ ?) |: Option |: Base 108 | 109 | val effect: Emm[E, String] = for { 110 | first <- readName.liftM[E] 111 | last <- readName.liftM[E] 112 | 113 | name <- (if ((first.length * last.length) < 20) Some(s"$first $last") else None).liftM[E] 114 | 115 | _ <- (if (name == "Daniel Spiewak") -\/("your kind isn't welcome here") else \/-(())).liftM[E] 116 | 117 | _ <- log(s"successfully read in $name").liftM[E] 118 | } yield name 119 | ``` 120 | 121 | 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: 122 | 123 | ```scala 124 | import scalaz._ 125 | import scalaz.concurrent.Task 126 | import scalaz.syntax.monad._ 127 | 128 | def readName: Task[String] = ??? 129 | def log(msg: String): Task[Unit] = ??? 130 | 131 | val effect: OptionT[EitherT[Task, String, ?], String] = for { 132 | first <- readName.liftM[EitherT[?[_], String, ?]].liftM[OptionT] 133 | last <- readName.liftM[(EitherT[?[_], String, ?]].liftM[OptionT] 134 | 135 | name <- if ((first.length * last.length) < 20) 136 | OptionT.some[EitherT[Task, String, ?], String](s"$first $last") 137 | else 138 | OptionT.none[EitherT[Task, String, ?], String] 139 | 140 | _ <- (if (name == "Daniel Spiewak") 141 | EitherT.fromDisjunction[Task](\/.left[String, Unit]("your kind isn't welcome here")) 142 | else 143 | EitherT.fromDisjunction[Task](\/.right[String, Unit](()))).liftM[OptionT] 144 | 145 | _ <- log(s"successfully read in $name").liftM[EitherT[?[_], String, ?]].liftM[OptionT] 146 | } yield name 147 | ``` 148 | 149 | 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. 150 | 151 | 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`! 152 | 153 | ## API 154 | 155 | The following API is provided. For starters, the following pair of functions are implicitly provided to lift values into the effect stack: 156 | 157 | - `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`. 158 | - `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]` 159 | - `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. 160 | 161 | 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._`). 162 | 163 | The `Emm` monad itself provides the following (effective) API: 164 | 165 | - `map[B](A => B): Emm[C, B]` – Conventional functor map. Transforms the value within the effect 166 | - `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. 167 | - `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`. 168 | - `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. 169 | - `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. 170 | - `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`. 171 | 172 | ## Requirements 173 | 174 | 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): 175 | 176 | - `Applicative` 177 | - `FlatMap` 178 | - `Functor` 179 | - `Traverse` 180 | 181 | ### Invalid and Partially-Valid Stacks 182 | 183 | Constraints which are not required to evaluate a given function are not assumed. For example, consider the following effect stack: 184 | 185 | ```scala 186 | type E = Option |: Task |: Base 187 | 188 | val effect = Option(42).liftM[E] 189 | ``` 190 | 191 | If you attempt to run `flatMap` on this effect stack, you will run into problems: 192 | 193 | ```scala 194 | effect flatMapM { i => if (i < 20) None else Some(i * 2) } // does not compile! 195 | ``` 196 | 197 | 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: 198 | 199 | ```scala 200 | val effect2: Option[Task[Int]] = Some(Task now 42) 201 | 202 | val mapped: Option[Task[Option[Task[Int]]]] = effect2 map { t => 203 | t map { i => 204 | if (i < 20) None else Some(Task now (i * 2)) 205 | } 206 | } 207 | 208 | val result: Option[Task[Int]] = mapped.flatten // ?????? 209 | ``` 210 | 211 | 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: 212 | 213 | ```scala 214 | Option[Task[Option[Task[Int]]]] => Option[Option[Task[Task[Int]]]] => Option[Task[Task[Int]]] => Option[Task[Int]] 215 | ``` 216 | 217 | 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. 218 | 219 | 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]`. 220 | 221 | 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`! 222 | 223 | ```scala 224 | type E = Option |: Task |: Base 225 | 226 | val effect = Option(42).liftM[E] 227 | 228 | val effect2 = effect map { _ * 2 } // no problemo! 229 | ``` 230 | 231 | 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`. 232 | 233 | ## Limitations 234 | 235 | Maybe this section should be nearer to the top... Oh well. 236 | 237 | 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`. 238 | 239 | Specifically, the following *kinds* of type constructors are accepted (i.e. will be fully functional in any position of an effect stack): 240 | 241 | - `* -> *` – Examples: `Option`, `List`, `Task` 242 | - `* x * -> *` – Examples: `Either`, `State` (with caveats), `Writer` (more caveats), `Reader` (sorry, still caveated) 243 | - `* x * x * -> *` – Examples: *No idea* 244 | - `(* -> *) x * -> *` – Examples: `Free`, `OptionT`, `ListT`, `StreamT` 245 | - `(* -> *) x * x * -> *` – Examples: *uh...* 246 | - `(* -> *) x * x * x * -> *` – Examples: `IndexedStateT` (sort of) 247 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | lazy val commonSettings = Seq( 2 | organization := "com.codecommit", 3 | 4 | licenses += ("Apache-2.0", url("http://www.apache.org/licenses/")), 5 | 6 | scalaVersion := "2.11.7", 7 | 8 | crossScalaVersions := Seq(scalaVersion.value), 9 | 10 | shimsVersion := "0.3", 11 | 12 | libraryDependencies += "org.specs2" %% "specs2-core" % "3.7" % "test", 13 | 14 | addCompilerPlugin("org.spire-math" % "kind-projector" % "0.7.1" cross CrossVersion.binary), 15 | 16 | scalacOptions += "-language:_", // I really can't be bothered with SIP-18 17 | scalacOptions += "-Ybackend:GenBCode", 18 | 19 | // scalacOptions += "-Xlog-implicits", 20 | 21 | scalacOptions in Test += "-Yrangepos", 22 | 23 | isSnapshot := version.value endsWith "SNAPSHOT", // so… sonatype doesn't like git hash snapshots 24 | 25 | publishMavenStyle := true, 26 | pomIncludeRepository := { _ => false }, 27 | 28 | sonatypeProfileName := "com.codecommit", 29 | 30 | pomExtra := 31 | 32 | 33 | djspiewak 34 | Daniel Spiewak 35 | http://www.codecommit.com 36 | 37 | 38 | alissapajer 39 | Alissa Pajer 40 | 41 | , 42 | 43 | homepage := Some(url("https://github.com/djspiewak/emm")), 44 | 45 | scmInfo := Some(ScmInfo(url("https://github.com/djspiewak/emm"), 46 | "git@github.com:djspiewak/emm.git"))) 47 | 48 | lazy val root = project.in(file(".")).settings(commonSettings: _*).aggregate(core, cats, scalaz71, scalaz72).settings( 49 | name := "emm", 50 | 51 | publish := (), 52 | publishLocal := (), 53 | publishArtifact := false) 54 | 55 | lazy val core = project.in(file("core")).settings(commonSettings: _*) 56 | lazy val cats = project.in(file("cats")).settings(commonSettings: _*).dependsOn(core) 57 | 58 | lazy val scalaz72 = project.in(file("scalaz72")).settings(commonSettings: _*).dependsOn(core) 59 | lazy val scalaz71 = project.in(file("scalaz71")).settings(commonSettings: _*).dependsOn(core) 60 | 61 | enablePlugins(GitVersioning) 62 | 63 | val ReleaseTag = """^v([\d\.]+)$""".r 64 | 65 | git.baseVersion := "0.2" 66 | 67 | git.gitTagToVersionNumber := { 68 | case ReleaseTag(version) => Some(version) 69 | case _ => None 70 | } 71 | 72 | git.formattedShaVersion := { 73 | val suffix = git.makeUncommittedSignifierSuffix(git.gitUncommittedChanges.value, git.uncommittedSignifier.value) 74 | 75 | git.gitHeadCommit.value map { _.substring(0, 7) } map { sha => 76 | git.baseVersion.value + "-" + sha + suffix 77 | } 78 | } 79 | 80 | git.gitUncommittedChanges := "git status -s".!!.trim.length > 0 81 | -------------------------------------------------------------------------------- /cats/build.sbt: -------------------------------------------------------------------------------- 1 | name := "emm-cats" 2 | 3 | libraryDependencies ++= Seq( 4 | "com.codecommit" %% "shims-cats" % shimsVersion.value, 5 | 6 | "org.scalaz" %% "scalaz-core" % "7.1.6" % "test") 7 | -------------------------------------------------------------------------------- /cats/src/main/scala/emm/compat/cats.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package compat 3 | 4 | import effects._ 5 | import properties._ 6 | 7 | import _root_.cats.~> 8 | import _root_.cats.data.Kleisli 9 | 10 | private[emm] object Shared { 11 | 12 | implicit def functor[F <: Effects](implicit F: Mapper[F]): _root_.cats.Functor[F#Point] = new _root_.cats.Functor[F#Point] { 13 | def map[A, B](fa: F#Point[A])(f: A => B): F#Point[B] = F.map(fa)(f) 14 | } 15 | 16 | implicit def monad[F <: Effects](implicit FM: Mapper[F], FB: Binder[F]): _root_.cats.Monad[F#Point] = new _root_.cats.Monad[F#Point] { 17 | def pure[A](a: A) = FM.point(a) 18 | override def map[A, B](fa: F#Point[A])(f: A => B): F#Point[B] = FM.map(fa)(f) 19 | def flatMap[A, B](fa: F#Point[A])(f: A => F#Point[B]): F#Point[B] = FB.bind(fa)(f) 20 | } 21 | } 22 | 23 | private[emm] trait BinderShims { 24 | 25 | 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] { 26 | import Shared.monad 27 | 28 | def bind[A, B](fca: CC[A])(f: A => CC[B]): CC[B] = { 29 | val back = NAP.unpack(fca) flatMap { ca => 30 | 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])) 31 | 32 | ptta map { tta => TB.bind(tta) { a => a } } 33 | } 34 | 35 | NAP.pack(back) 36 | } 37 | } 38 | } 39 | 40 | private[emm] trait LifterShims { 41 | 42 | 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] { 43 | type Out = A 44 | 45 | import Shared.functor 46 | 47 | def apply(e: Kleisli[λ[X => X], Z, A]): CC[Out] = { 48 | val t = new (λ[X => X] ~> F#Point) { 49 | def apply[A](a: A): F#Point[A] = F.point(a) 50 | } 51 | 52 | NAP.pack(e.transform[F#Point](t) map T.point) 53 | } 54 | } 55 | 56 | 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] { 57 | type Out = F.Out 58 | 59 | def apply(e: E): CC[Out] = 60 | NAP.pack(Kleisli[F#Point, Z, T#Point[F.Out]]({ _ => FM.map(F(e)) { a => T.point(a) } })) 61 | } 62 | 63 | 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] { 64 | type Out = T.Out 65 | 66 | def apply(e: E): CC[Out] = NAP.pack(Kleisli[F#Point, Z, T#Point[T.Out]] { _ => F.point(T(e)) }) 67 | } 68 | } 69 | 70 | private[emm] trait MapperShims { 71 | 72 | // we require a Binder[F] because we cannot derive a consistent Applicative from Mapper[F] 73 | 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] { 74 | import Shared.monad 75 | 76 | def point[A](a: A): CC[A] = NAP.pack(Kleisli.pure[F#Point, Z, T#Point[A]](T.point(a))) 77 | 78 | def map[A, B](fa: CC[A])(f: A => B): CC[B] = 79 | NAP.pack(NAP.unpack(fa) map { ta => T.map(ta)(f) }) 80 | } 81 | } 82 | 83 | object cats extends shims.Implicits with BinderShims with LifterShims with MapperShims -------------------------------------------------------------------------------- /cats/src/test/scala/emm/ApplicativeSpecs.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | 3 | import org.specs2.mutable._ 4 | 5 | import cats._ 6 | import cats.std.list._ 7 | import cats.std.option._ 8 | 9 | /*object ApplicativeSpecs extends Specification { 10 | "derived applicative" should { 11 | "be consistent with bind in Option |: List" in { 12 | type E = Option |: List |: Base 13 | 14 | val a = Emm[E, Int](None) 15 | val b = Emm[E, Int => Int](Some(Nil)) 16 | 17 | // swap the following two blocks and the test will fail 18 | 19 | // val A = Emm.applicativeInstance[E] 20 | // val M = Emm.monadInstance[E] 21 | 22 | val A = Applicative[Emm[E, ?]] 23 | val M = Monad[Emm[E, ?]] 24 | 25 | A.ap(a)(b) mustEqual (M.flatMap(b) { b => M.map(a)(b) }) 26 | M.ap(a)(b) mustEqual (M.flatMap(b) { b => M.map(a)(b) }) 27 | } 28 | } 29 | }*/ 30 | -------------------------------------------------------------------------------- /cats/src/test/scala/emm/BinderSpecs.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | 3 | import emm.compat.cats._ 4 | 5 | import org.specs2.mutable._ 6 | 7 | import cats._ 8 | import cats.data._ 9 | import cats.free.Free 10 | import cats.std.list._ 11 | import cats.std.option._ 12 | import cats.std.function._ 13 | 14 | import scalaz.concurrent.Task 15 | 16 | object BinderSpecs extends Specification with TestHelpers { 17 | 18 | "simple effect composition with binder" should { 19 | 20 | "allow binding" in { 21 | type E = List |: Option |: Base 22 | 23 | val e = for { 24 | v <- List(1, 2, 3, 4).liftM[E] 25 | v2 <- (Some(v) filter { _ % 2 == 0 }).liftM[E] 26 | } yield v2 27 | 28 | e mustEqual Emm[E, Int](List(None, Some(2), None, Some(4))) 29 | } 30 | 31 | "allow binding over a Option of Kleisli of List" in { 32 | type E = Option |: Kleisli[?[_], Int, ?] -|: List |: Base 33 | 34 | import effects._ 35 | import properties._ 36 | 37 | "foobar".pointM[E].flatMap(x => (x + "baz").pointM[E]).run.run(42) mustEqual Some(List("foobarbaz")) 38 | } 39 | 40 | "allow binding over a Option of List of Kleisli" in { 41 | type E = Option |: List |: Kleisli[?[_], Int, ?] -|: Base 42 | 43 | "foobar".pointM[E].flatMap(x => (x + "baz").pointM[E]).run.run(42) mustEqual Some(List("foobarbaz")) 44 | } 45 | 46 | "bind over a stack that contains a partially-applied arity-2 constructor" in { 47 | type E = (String Xor ?) |: Base 48 | 49 | 42.pointM[E] flatMap { _ => "foo".pointM[E] } mustEqual Emm[E, String](Xor.right("foo")) 50 | } 51 | 52 | "bind over a stack that contains a partially-applied arity-2 higher-order constructor" in { 53 | "base" >> { 54 | type E = Free[List, ?] |: Base 55 | 56 | (42.pointM[E] flatMap { _ => "foo".pointM[E] } run).runM(identity) mustEqual List("foo") 57 | } 58 | 59 | "inner" >> { 60 | type E = Option |: Free[List, ?] |: Base 61 | 62 | (42.pointM[E] flatMap { _ => "foo".pointM[E] } run) must beLike { 63 | case Some(f) => f.runM(identity) mustEqual List("foo") 64 | } 65 | } 66 | 67 | "outer" >> { 68 | type E = Free[List, ?] |: Option |: Base 69 | 70 | (42.pointM[E] flatMap { _ => "foo".pointM[E] } run).runM(identity) mustEqual List(Option("foo")) 71 | } 72 | } 73 | 74 | "bind over a stack that contains state" in { 75 | "inner" >> { 76 | type E = Option |: StateT[?[_], String, ?] -|: Base 77 | 78 | (42.pointM[E] flatMap { _ => "foo".pointM[E] }).run.runA("blah") must beSome("foo") 79 | } 80 | 81 | "mid" >> { 82 | type E = List |: StateT[?[_], String, ?] -|: Option |: Base 83 | 84 | (42.pointM[E] flatMap { _ => "foo".pointM[E] }).run.runA("blah") mustEqual List(Some("foo")) 85 | } 86 | } 87 | 88 | "enable flatMapM in any direction" in { 89 | type E = List |: Option |: Base 90 | 91 | val e1 = List(1, 2, 3, 4).liftM[E] 92 | val e2 = e1 flatMapM { v => Some(v) filter { _ % 2 == 0 } } 93 | val e3 = e2 flatMapM { v => List(v, v) } 94 | 95 | e3 mustEqual Emm[E, Int](List(None, Some(2), Some(2), None, Some(4), Some(4))) 96 | } 97 | 98 | "allow flatMapM on a stack containing an arity-2 constructor" in { 99 | type E = List |: (String Xor ?) |: Base 100 | 101 | val e1 = List(1, 2, 3, 4).liftM[E] 102 | val e2 = e1 flatMapM { v => if (v % 2 == 0) Xor.right(v) else Xor.left("that's... odd") } 103 | val e3 = e2 flatMapM { v => List(v, v) } 104 | 105 | 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))) 106 | } 107 | 108 | "allow flatMapM on a stack containing a higher-order arity-2 constructor" in { 109 | type E = List |: Free[Option, ?] |: Base 110 | 111 | val e1 = List(1, 2, 3, 4).liftM[E] 112 | val e2 = e1 flatMapM { v => if (v % 2 == 0) Free.pure[Option, Int](v) else Free.liftF[Option, Int](None) } 113 | val e3 = e2 flatMapM { v => List(v, v) } 114 | 115 | e3.run must beLike { 116 | case List(f1, f2, f3, f4, f5, f6) => { 117 | f1.runM(identity) mustEqual None 118 | f2.runM(identity) mustEqual Some(2) 119 | f3.runM(identity) mustEqual Some(2) 120 | f4.runM(identity) mustEqual None 121 | f5.runM(identity) mustEqual Some(4) 122 | f6.runM(identity) mustEqual Some(4) 123 | } 124 | } 125 | } 126 | } 127 | 128 | "non-traversable effect composition with binder" should { 129 | 130 | "allow binding where the non-traversable effect is outermost" in { 131 | type E = Task |: Option |: Base 132 | val opt: Option[Int] = Some(42) 133 | 134 | var sink = 0 135 | 136 | val e = for { 137 | i <- opt.liftM[E] 138 | _ <- (Task delay { sink += i }).liftM[E] 139 | } yield () 140 | 141 | e.run.run 142 | 143 | sink mustEqual 42 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /cats/src/test/scala/emm/ExpanderCollapserSpecs.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | 3 | import emm.compat.cats._ 4 | 5 | import org.specs2.mutable._ 6 | 7 | import cats._ 8 | import cats.data._ 9 | import cats.free.Free 10 | import cats.std.list._ 11 | import cats.std.option._ 12 | import cats.std.function._ 13 | 14 | import scalaz.concurrent.Task 15 | import scalaz.\/- 16 | 17 | object ExpanderCollapserSpecs extends Specification with TestHelpers { 18 | 19 | "basic simple effect composition" should { 20 | 21 | "define pointM" in { 22 | 42.pointM[Option |: List |: Base] mustEqual Emm[Option |: List |: Base, Int](Option(List(42))) 23 | } 24 | } 25 | 26 | "non-traversable effect composition with expander and collapser" should { 27 | 28 | "enable access to the base of the stack" in { 29 | type E = Task |: Option |: Base 30 | val opt: Option[Int] = None 31 | 32 | // this type infers better than a single function 33 | val e = opt.liftM[E].expand map { _ getOrElse 12 } 34 | 35 | e.run.run mustEqual 12 36 | } 37 | 38 | "allow both expansion and collapse of base" in { 39 | type E = Task |: Option |: Base 40 | val opt: Option[Int] = None 41 | 42 | // this type infers better than a single function 43 | val e = opt.liftM[E].expand map { _ orElse Some(24) } collapse 44 | 45 | e.run.run must beSome(24) 46 | } 47 | 48 | "allow both expansion and collapse of base with an arity-2 constructor" in { 49 | "inner" >> { 50 | type E = Task |: (String Xor ?) |: Base 51 | 52 | val e = (Task now 42).liftM[E].expand map { _.swap } collapse 53 | 54 | e.run.run mustEqual Xor.left(42) 55 | } 56 | 57 | "outer" >> { 58 | type E = (String Xor ?) |: Task |: Base 59 | 60 | val e = (Task now 42).liftM[E].expand map { _.attempt } collapse 61 | 62 | e.run must beLike { 63 | case Xor.Right(t) => t.run mustEqual \/-(42) 64 | } 65 | } 66 | } 67 | 68 | "allow both expansion and collapse of base with a higher-order arity-2 constructor" in { 69 | val toList = new (Option ~> List) { 70 | def apply[A](xs: Option[A]) = xs.toList 71 | } 72 | 73 | "inner" >> { 74 | type E = Task |: Free[Option, ?] |: Base 75 | 76 | val e = (Task now 42).liftM[E].expand map { _ mapSuspension toList } collapse 77 | 78 | e.run.run.runM(identity) mustEqual List(42) 79 | } 80 | 81 | "outer" >> { 82 | type E = Free[Option, ?] |: Task |: Base 83 | 84 | val e = (Task now 42).liftM[E].expand map { _.attempt } collapse 85 | 86 | e.run.runM(identity) must beLike { 87 | case Some(t) => t.run mustEqual \/-(42) 88 | } 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /cats/src/test/scala/emm/FutureSpecs.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | 3 | import emm.compat.cats._ 4 | 5 | import org.specs2.mutable._ 6 | 7 | import cats.std.list._ 8 | import cats.std.option._ 9 | import cats.std.future._ 10 | 11 | import scala.concurrent.{Await, Future} 12 | import scala.concurrent.duration._ 13 | 14 | object FutureSpecs extends Specification { 15 | implicit val ec = scala.concurrent.ExecutionContext.global 16 | 17 | "emm with future" should { 18 | "compose with list and option" in { 19 | type E = Future |: Option |: List |: Base 20 | 21 | val bam = for { 22 | f <- Future(3).liftM[E] 23 | o <- Option(2).liftM[E] 24 | l <- List(1, 1).liftM[E] 25 | } yield f + o + l 26 | 27 | Await.result(bam.run, 20 seconds) must beSome(List(6, 6)) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cats/src/test/scala/emm/LifterSpecs.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | 3 | import emm.compat.cats._ 4 | 5 | import org.specs2.mutable._ 6 | 7 | import cats._ 8 | import cats.data._ 9 | import cats.std.list._ 10 | import cats.std.option._ 11 | import cats.free.Free 12 | 13 | import scalaz.concurrent.Task 14 | 15 | object LifterSpecs extends Specification with TestHelpers { 16 | 17 | "simple effect composition with lifter" should { 18 | 19 | "allow lifting in either direction" in { 20 | val opt: Option[Int] = Some(42) 21 | 22 | opt.liftM[Option |: List |: Base] must haveType[Emm[Option |: List |: Base, Int]].attempt 23 | opt.liftM[List |: Option |: Base] must haveType[Emm[List |: Option |: Base, Int]].attempt 24 | } 25 | 26 | "lift into an effect stack of depth three" in { 27 | type E = Task |: List |: Option |: Base 28 | 29 | Option(42).liftM[E].run.run mustEqual List(Option(42)) 30 | List(42).liftM[E].run.run mustEqual List(Option(42)) 31 | (Task now 42).liftM[E].run.run mustEqual List(Option(42)) 32 | } 33 | 34 | "lift into a stack that contains a partially-applied arity-2 constructor" in { 35 | "inner" >> { 36 | type E = Option |: (String Xor ?) |: Base 37 | 38 | Xor.right[String, Int](42).liftM[E] mustEqual Emm[E, Int](Option(Xor.right(42))) 39 | Xor.left[String, Int]("fuuuuuu").liftM[E] mustEqual Emm[E, Int](Option(Xor.left("fuuuuuu"))) 40 | } 41 | 42 | "outer" >> { 43 | type E = (String Xor ?) |: Option |: Base 44 | 45 | Xor.right[String, Int](42).liftM[E] mustEqual Emm[E, Int](Xor.right(Option(42))) 46 | Xor.left[String, Int]("fuuuuuu").liftM[E] mustEqual Emm[E, Int](Xor.left("fuuuuuu")) 47 | } 48 | } 49 | 50 | "lift into a stack that contains a partially-applied arity-2 higher-order constructor" in { 51 | "inner" >> { 52 | type E = Option |: Free[List, ?] |: Base 53 | 54 | Free.pure[List, Int](42).liftM[E].run must beLike { 55 | case Some(f) => f runM identity mustEqual List(42) 56 | } 57 | 58 | Option(42).liftM[E].run must beLike { 59 | case Some(f) => f runM identity mustEqual List(42) 60 | } 61 | } 62 | 63 | "outer" >> { 64 | type E = Free[List, ?] |: Option |: Base 65 | 66 | Free.pure[List, Int](42).liftM[E].run.runM(identity) mustEqual List(Option(42)) 67 | Option(42).liftM[E].run.runM(identity) mustEqual List(Option(42)) 68 | } 69 | } 70 | 71 | "lift into a stack that contains a partially-applied arity-2 higher-order constructor and an arity-2 constructor" in { 72 | "inner" >> { 73 | type E = (String Xor ?) |: Free[List, ?] |: Base 74 | 75 | Free.pure[List, Int](42).liftM[E].run must beLike { 76 | case Xor.Right(f) => f runM identity mustEqual List(42) 77 | } 78 | 79 | Xor.right[String, Int](42).liftM[E].run must beLike { 80 | case Xor.Right(f) => f runM identity mustEqual List(42) 81 | } 82 | } 83 | 84 | "outer" >> { 85 | type E = Free[List, ?] |: (String Xor ?) |: Base 86 | 87 | Free.pure[List, Int](42).liftM[E].run.runM(identity) mustEqual List(Xor.right(42)) 88 | Xor.right[String, Int](42).liftM[E].run.runM(identity) mustEqual List(Xor.right(42)) 89 | } 90 | } 91 | 92 | "lift into a stack that contains a kleisli" in { 93 | import cats.data.Kleisli 94 | 95 | "inner" >> { 96 | type E = Option |: Kleisli[?[_], String, ?] -|: Base 97 | 98 | Kleisli.pure[λ[X => X], String, Int](12).liftM[E].run.run("foo") must beSome(12) 99 | Option(42).liftM[E].run.run("foo") must beSome(42) 100 | } 101 | 102 | "outer" >> { 103 | type E = Free[List, ?] |: Option |: Base 104 | 105 | Free.pure[List, Int](42).liftM[E].run.runM(identity) mustEqual List(Option(42)) 106 | Option(42).liftM[E].run.runM(identity) mustEqual List(Option(42)) 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /cats/src/test/scala/emm/MapperSpecs.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | 3 | import emm.compat.cats._ 4 | 5 | import org.specs2.mutable._ 6 | 7 | import cats._ 8 | import cats.data._ 9 | import cats.std.list._ 10 | import cats.std.option._ 11 | 12 | import scalaz.concurrent.Task 13 | 14 | object MapperSpecs extends Specification with TestHelpers { 15 | 16 | "simple effect composition with mapper" should { 17 | 18 | "allow mapping" in { 19 | val opt: Option[Int] = Some(42) 20 | val e = opt.liftM[List |: Option |: Base] 21 | 22 | e map (2 *) mustEqual Emm[List |: Option |: Base, Int](List(Some(84))) 23 | } 24 | 25 | "allow mapping over a Kleisli" in { 26 | type E = Option |: Kleisli[?[_], Int, ?] -|: Base 27 | 28 | "foobar".pointM[E].map(_ + "baz").run.run(42) must beSome("foobarbaz") 29 | } 30 | 31 | "allow mapping over a List of Option of Kleisli" in { 32 | type E = List |: Option |: Kleisli[?[_], Int, ?] -|: Base 33 | 34 | "foobar".pointM[E].map(_ + "baz").run.run(42) mustEqual List(Some("foobarbaz")) 35 | } 36 | 37 | "allow mapping over a Option of Kleisli of List" in { 38 | type E = Option |: Kleisli[?[_], Int, ?] -|: List |: Base 39 | 40 | "foobar".pointM[E].map(_ + "baz").run.run(42) mustEqual Some(List("foobarbaz")) 41 | } 42 | } 43 | 44 | "non-traversable effect composition with mapper" should { 45 | 46 | "allow mapping in either direction" in { 47 | val opt: Option[Int] = Some(42) 48 | 49 | opt.liftM[Task |: Option |: Base] map (2 *) 50 | opt.liftM[Option |: Task |: Base] map (2 *) 51 | 52 | ok 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cats/src/test/scala/emm/TestHelpers.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | 3 | import org.specs2.matcher._ 4 | 5 | import cats._ 6 | import cats.data._ 7 | import cats.free.Free 8 | 9 | import scalaz.concurrent.Task 10 | 11 | import scala.reflect.runtime.universe.TypeTag 12 | 13 | 14 | trait TestHelpers { 15 | 16 | def haveType[A](implicit A: TypeTag[A]) = new { 17 | 18 | def attempt[B](implicit B: TypeTag[B]): Matcher[B] = new Matcher[B] { 19 | def apply[B2 <: B](s: Expectable[B2]) = 20 | 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) 21 | } 22 | } 23 | 24 | implicit val taskFlatMap: Monad[Task] = new Monad[Task] { 25 | import scalaz.concurrent.Future 26 | 27 | def pure[A](x: A): Task[A] = new Task(Future.delay(Task.Try(x))) 28 | 29 | def flatMap[A, B](fa: Task[A])(f: A => Task[B]): Task[B] = { 30 | fa.flatMap(f) 31 | } 32 | } 33 | 34 | implicit def freeTraverse[F[_]](implicit trF: Traverse[F]): Traverse[Free[F, ?]] = new Traverse[Free[F, ?]] { 35 | 36 | def traverse[G[_], A, B](fa: Free[F, A])(f: A => G[B])(implicit ap: Applicative[G]): G[Free[F, B]] = 37 | fa.resume match { 38 | case Xor.Left(s) => ap.map(trF.traverse(s)(fa => traverse[G, A ,B](fa)(f)))(ffa => Free.liftF(ffa).flatMap(a => a)) 39 | case Xor.Right(a) => ap.map(f(a))(Free.pure(_)) 40 | } 41 | 42 | def foldLeft[A, B](fa: Free[F, A], b: B)(f: (B, A) => B): B = ??? 43 | 44 | def foldRight[A, B](fa: Free[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = ??? 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /cats/src/test/scala/emm/WrapperSpecs.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | 3 | import emm.compat.cats._ 4 | 5 | import org.specs2.mutable._ 6 | 7 | import cats._ 8 | import cats.data._ 9 | import cats.free.Free 10 | import cats.std.option._ 11 | import cats.std.list._ 12 | 13 | object WrapperSpecs extends Specification with TestHelpers { 14 | 15 | "simple effect composition with wrapper" should { 16 | 17 | "allow wrapping of two paired constructors" in { 18 | Option(List(42)).wrapM must haveType[Emm[Option |: List |: Base, Int]].attempt 19 | Option(List(42)).wrapM[Option |: List |: Base] must haveType[Emm[Option |: List |: Base, Int]].attempt 20 | } 21 | 22 | "allow wrapping of two paired constructors where one has arity-2" in { 23 | "inner" >> { 24 | type E = Option |: (String Xor ?) |: Base 25 | 26 | Option(Xor.right[String, Int](42)).wrapM must haveType[Emm[Option |: (String Xor ?) |: Base, Int]].attempt 27 | Option(Xor.right[String, Int](42)).wrapM[E] must haveType[Emm[Option |: (String Xor ?) |: Base, Int]].attempt 28 | } 29 | 30 | "outer" >> { 31 | type E = (String Xor ?) |: Option |: Base 32 | 33 | Xor.right[String, Option[Int]](Option(42)).wrapM must haveType[Emm[(String Xor ?) |: Option |: Base, Int]].attempt 34 | Xor.right[String, Option[Int]](Option(42)).wrapM[E] must haveType[Emm[(String Xor ?) |: Option |: Base, Int]].attempt 35 | } 36 | } 37 | 38 | "allow wrapping of two paired constructors where one is higher-order and has arity-2" in { 39 | "inner" >> { 40 | type E = Option |: Free[List, ?] |: Base 41 | 42 | Option(Free.pure[List, Int](42)).wrapM must haveType[Emm[Option |: Free[List, ?] |: Base, Int]].attempt 43 | Option(Free.pure[List, Int](42)).wrapM[E] must haveType[Emm[Option |: Free[List, ?] |: Base, Int]].attempt 44 | } 45 | 46 | "outer" >> { 47 | type E = Free[List, ?] |: Option |: Base 48 | 49 | Free.pure[List, Option[Int]](Option(42)).wrapM must haveType[Emm[Free[List, ?] |: Option |: Base, Int]].attempt 50 | Free.pure[List, Option[Int]](Option(42)).wrapM[E] must haveType[Emm[Free[List, ?] |: Option |: Base, Int]].attempt 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/build.sbt: -------------------------------------------------------------------------------- 1 | name := "emm-core" 2 | 3 | libraryDependencies += "com.codecommit" %% "shims-core" % shimsVersion.value -------------------------------------------------------------------------------- /core/src/main/scala/emm/Emm.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | 3 | final case class Emm[C <: Effects, A](run: C#Point[A]) { 4 | import effects._ 5 | 6 | def map[B](f: A => B)(implicit C: Mapper[C]): Emm[C, B] = Emm(C.map(run)(f)) 7 | 8 | def flatMap[B](f: A => Emm[C, B])(implicit B: Binder[C]): Emm[C, B] = 9 | Emm(B.bind(run) { a => f(a).run }) 10 | 11 | def flatMapM[E](f: A => E)(implicit E: Lifter[E, C], B: Binder[C]): Emm[C, E.Out] = 12 | flatMap { a => Emm(E(f(a))) } 13 | 14 | def expand(implicit C: Expander[C]): Emm[C.Out, C.CC[A]] = Emm(C(run)) 15 | 16 | def collapse(implicit C: Collapser[A, C]): Emm[C.Out, C.A] = Emm(C(run)) 17 | } 18 | 19 | object Emm { 20 | import effects._ 21 | 22 | def point[C <: Effects, A](a: A)(implicit M: Mapper[C]): Emm[C, A] = Emm[C, A](M.point(a)) 23 | 24 | def lift[C <: Effects, A](a: A)(implicit L: Lifter[A, C]): Emm[C, L.Out] = Emm(L(a)) 25 | 26 | def wrap[C <: Effects, A](a: A)(implicit W: Wrapper[A, C]): Emm[C, W.A] = Emm[C, W.A](W(a)) 27 | } 28 | 29 | /*trait EmmLowPriorityImplicits1 { 30 | import effects._ 31 | 32 | implicit def functorInstance[C <: Effects](implicit C: Mapper[C]): Functor[Emm[C, ?]] = new Functor[Emm[C, ?]] { 33 | 34 | def map[A, B](fa: Emm[C, A])(f: A => B): Emm[C, B] = new Emm(C.map(fa.run)(f)) 35 | } 36 | } 37 | 38 | trait EmmLowPriorityImplicits2 extends EmmLowPriorityImplicits1 { 39 | import effects._ 40 | 41 | implicit def monadInstance[C <: Effects : Mapper : Binder]: Monad[Emm[C, ?]] = new Monad[Emm[C, ?]] { 42 | 43 | def pure[A](a: A): Emm[C, A] = new Emm(implicitly[Mapper[C]].point(a)) 44 | 45 | def flatMap[A, B](fa: Emm[C, A])(f: A => Emm[C, B]): Emm[C, B] = fa flatMap f 46 | } 47 | } 48 | 49 | object Emm extends EmmLowPriorityImplicits2 { 50 | import effects._ 51 | 52 | implicit def traverseInstance[C <: Effects](implicit C: Traverser[C]): Traverse[Emm[C, ?]] = new Traverse[Emm[C, ?]] { 53 | def traverse[G[_]: Applicative, A, B](fa: Emm[C, A])(f: A => G[B]): G[Emm[C, B]] = 54 | Applicative[G].map(C.traverse(fa.run)(f)) { new Emm(_) } 55 | 56 | def foldLeft[A, B](fa: Emm[C, A], b: B)(f: (B, A) => B): B = C.foldLeft(fa.run, b)(f) 57 | 58 | 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) 59 | 60 | } 61 | }*/ 62 | -------------------------------------------------------------------------------- /core/src/main/scala/emm/effects.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | 3 | // there's a bug in scalac's cyclic checking with open recursion and type constructors with arity > 1 4 | trait Partial { 5 | type Apply[F[_]] 6 | } 7 | 8 | sealed trait Effects { 9 | type Point[A] = Build[λ[X => X], A] 10 | 11 | type Build[CC[_], A] = Inner[A]#Apply[CC] 12 | 13 | type Inner[A] <: Partial 14 | 15 | type Append[E <: Effects] <: Effects 16 | } 17 | 18 | sealed trait |:[F[_], T <: Effects] extends Effects { 19 | type Inner[A] = Partial { type Apply[CC[_]] = T#Inner[A]#Apply[λ[X => CC[F[X]]]] } 20 | type Append[E <: Effects] = F |: T#Append[E] 21 | } 22 | 23 | sealed trait -|:[F[_[_], _], T <: Effects] extends Effects { 24 | type Inner[A] = Partial { type Apply[CC[_]] = F[CC, T#Inner[A]#Apply[λ[X => X]]] } 25 | type Append[E <: Effects] = F -|: T#Append[E] 26 | } 27 | 28 | sealed trait Base extends Effects { 29 | type Inner[A] = Partial { type Apply[F[_]] = F[A] } 30 | type Append[E <: Effects] = E 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/scala/emm/effects/Binder.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package effects 3 | 4 | import shims.{Applicative, FlatMap, Functor, Monad, Traverse} 5 | import scala.annotation.implicitNotFound 6 | 7 | import properties._ 8 | 9 | @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") 10 | trait Binder[C <: Effects] { 11 | type CC[A] = C#Point[A] 12 | 13 | def bind[A, B](cca: CC[A])(f: A => CC[B]): CC[B] 14 | } 15 | 16 | object Binder { 17 | 18 | implicit def base: Binder[Base] = new Binder[Base] { 19 | def bind[A, B](fa: A)(f: A => B): B = f(fa) 20 | } 21 | 22 | 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] { 23 | 24 | def bind[A, B](fca: CC[A])(f: A => CC[B]): CC[B] = { 25 | val back = B.flatMap(NN.unpack(fca)) { ca => 26 | val fcaca = T.traverse(ca) { a => NN.unpack(f(a)) } 27 | 28 | F.map(fcaca) { caca => C.bind(caca) { a => a } } 29 | } 30 | 31 | NN.pack(back) 32 | } 33 | } 34 | 35 | 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] 36 | 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] 37 | 38 | 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] 39 | 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] 40 | 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] 41 | 42 | 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] { 43 | 44 | def bind[A, B](fca: CC[A])(f: A => CC[B]): CC[B] = { 45 | val back: Pivot[F#Point, T#Point[B]] = Pivot.flatMap(NAP.unpack(fca)) { ca => 46 | 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))) 47 | 48 | Pivot.map(ptta)(tta => TB.bind(tta)(a => a)) 49 | } 50 | 51 | NAP.pack(back) 52 | } 53 | } 54 | 55 | 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] 56 | 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] 57 | } 58 | -------------------------------------------------------------------------------- /core/src/main/scala/emm/effects/Collapser.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package effects 3 | 4 | import shims.{Applicative, FlatMap, Functor, Monad, Traverse} 5 | import scala.annotation.implicitNotFound 6 | 7 | import properties._ 8 | 9 | trait Collapser[E, C <: Effects] { 10 | type A 11 | type Out <: Effects 12 | 13 | type Point[A] = C#Point[A] 14 | 15 | def apply(fa: Point[E]): Out#Point[A] 16 | } 17 | 18 | trait CollapserLowPriorityImplicits2 { 19 | 20 | def head1[F[_], A0]: Collapser.Aux[F[A0], Base, A0, F |: Base] 21 | 22 | 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] 23 | 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] 24 | 25 | def corecurse1[E, F[_], C <: Effects](implicit C: Collapser[E, C]): Collapser.Aux[E, F |: C, C.A, F |: C.Out] 26 | 27 | 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] 28 | 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] 29 | } 30 | 31 | object Collapser extends CollapserLowPriorityImplicits2 { 32 | type Aux[E, C <: Effects, A0, Out0 <: Effects] = Collapser[E, C] { type A = A0; type Out = Out0 } 33 | 34 | implicit def head1[F[_], A0]: Collapser.Aux[F[A0], Base, A0, F |: Base] = new Collapser[F[A0], Base] { 35 | type A = A0 36 | type Out = F |: Base 37 | 38 | def apply(fa: F[A]): F[A] = fa 39 | } 40 | 41 | 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] 42 | 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] 43 | 44 | implicit def headH1[F[_[_], _], G[_], A0]: Collapser.Aux[F[G, A0], Base, A0, F[G, ?] |: Base] = head1[F[G, ?], A0] 45 | 46 | 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] { 47 | type A = C.A 48 | type Out = F |: C.Out 49 | 50 | def apply(gca: Point[E]): Out#Point[A] = 51 | gca.asInstanceOf[Out#Point[A]] // already proven equivalent; evaluation requires a Functor 52 | } 53 | 54 | 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] 55 | 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] 56 | 57 | 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] 58 | 59 | // C == F ++ (Pivot -|: T) 60 | 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] { 61 | type A = T.A 62 | type Out = F#Append[Pivot -|: T.Out] 63 | 64 | def apply(gca: Point[E]): Out#Point[A] = 65 | gca.asInstanceOf[Out#Point[A]] // already proven equivalent; evaluation requires a Functor 66 | } 67 | 68 | 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] 69 | 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] 70 | } 71 | -------------------------------------------------------------------------------- /core/src/main/scala/emm/effects/Expander.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package effects 3 | 4 | import shims.{Applicative, FlatMap, Functor, Monad, Traverse} 5 | import scala.annotation.implicitNotFound 6 | 7 | import properties._ 8 | 9 | trait Expander[C <: Effects] { 10 | type CC[_] 11 | type Out <: Effects 12 | 13 | type Point[A] = C#Point[A] 14 | 15 | def apply[A](fa: Point[A]): Out#Point[CC[A]] 16 | } 17 | 18 | object Expander { 19 | type Aux[C <: Effects, CC0[_], Out0 <: Effects] = Expander[C] { type CC[A] = CC0[A]; type Out = Out0 } 20 | 21 | implicit def head1[F[_]]: Expander.Aux[F |: Base, F, Base] = new Expander[F |: Base] { 22 | type CC[A] = F[A] 23 | type Out = Base 24 | 25 | def apply[A](fa: F[A]): F[A] = fa 26 | } 27 | 28 | implicit def head2[F[_, _], F2[_, _], Z](implicit ev: Permute2[F, F2]): Expander.Aux[F2[Z, ?] |: Base, F2[Z, ?], Base] = head1[F2[Z, ?]] 29 | 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, ?]] 30 | 31 | implicit def headH1[F[_[_], _], G[_]]: Expander.Aux[F[G, ?] |: Base, F[G, ?], Base] = head1[F[G, ?]] 32 | 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, ?]] 33 | 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, ?]] 34 | 35 | implicit def corecurse1[F[_], C <: Effects](implicit C: Expander[C]): Expander.Aux[F |: C, C.CC, F |: C.Out] = new Expander[F |: C] { 36 | type CC[A] = C.CC[A] 37 | type Out = F |: C.Out 38 | 39 | def apply[A](gca: Point[A]): Out#Point[CC[A]] = 40 | gca.asInstanceOf[Out#Point[CC[A]]] // already proven equivalent; evaluation requires a Functor 41 | } 42 | 43 | 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] 44 | 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] 45 | 46 | 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] 47 | 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] 48 | 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] 49 | 50 | 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] { 51 | type CC[A] = Pivot[F#Point, A] 52 | type Out = Base 53 | 54 | def apply[A](gca: C#Point[A]): Out#Point[CC[A]] = 55 | gca.asInstanceOf[Out#Point[CC[A]]] // already proven equivalent; evaluation requires a Functor 56 | } 57 | 58 | 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) 59 | 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) 60 | 61 | // C == F ++ (Pivot -|: T) 62 | 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] { 63 | type CC[A] = T.CC[A] 64 | type Out = F#Append[Pivot -|: T.Out] 65 | 66 | def apply[A](gca: C#Point[A]): Out#Point[CC[A]] = 67 | gca.asInstanceOf[Out#Point[CC[A]]] // already proven equivalent; evaluation requires a Functor 68 | } 69 | 70 | 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] 71 | 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] 72 | } 73 | -------------------------------------------------------------------------------- /core/src/main/scala/emm/effects/Lifter.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package effects 3 | 4 | import shims.{Applicative, FlatMap, Functor, Monad, Traverse} 5 | import scala.annotation.implicitNotFound 6 | 7 | import properties._ 8 | 9 | @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}") 10 | trait Lifter[E, C <: Effects] { 11 | type Out 12 | type CC[A] = C#Point[A] 13 | 14 | def apply(e: E): CC[Out] 15 | } 16 | 17 | object Lifter { 18 | type Aux[E, C <: Effects, Out0] = Lifter[E, C] { type Out = Out0 } 19 | 20 | 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] { 21 | type Out = A 22 | 23 | def apply(fa: F[A]) = NN.pack(F.map(fa) { a => C.point(a) }) 24 | } 25 | 26 | 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] 27 | 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] 28 | 29 | 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] 30 | 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] 31 | 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] 32 | 33 | 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] { 34 | type Out = L.Out 35 | 36 | def apply(e: E) = NN.pack(F.point(L(e))) 37 | } 38 | 39 | 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] 40 | 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] 41 | 42 | 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] 43 | 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] 44 | 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] 45 | } -------------------------------------------------------------------------------- /core/src/main/scala/emm/effects/Mapper.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package effects 3 | 4 | import shims.{Applicative, FlatMap, Functor, Monad, Traverse} 5 | import scala.annotation.implicitNotFound 6 | 7 | import properties._ 8 | 9 | @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") 10 | trait Mapper[C <: Effects] { outer => 11 | type CC[A] = C#Point[A] 12 | 13 | def point[A](a: A): CC[A] 14 | 15 | def map[A, B](fa: CC[A])(f: A => B): CC[B] 16 | 17 | def functor: Functor[CC] = new Functor[CC] { 18 | def map[A, B](fa: CC[A])(f: A => B): CC[B] = outer.map(fa)(f) 19 | } 20 | } 21 | 22 | object Mapper { 23 | 24 | implicit def base: Mapper[Base] = new Mapper[Base] { 25 | def point[A](a: A) = a 26 | def map[A, B](fa: A)(f: A => B) = f(fa) 27 | } 28 | 29 | implicit def corecurse1[F[_], C <: Effects](implicit P: Mapper[C], NN: NonNested[C], F: Applicative[F]): Mapper[F |: C] = new Mapper[F |: C] { 30 | 31 | def point[A](a: A) = NN.pack(F.point(P.point(a))) 32 | 33 | def map[A, B](fa: CC[A])(f: A => B): CC[B] = 34 | NN.pack(F.map(NN.unpack(fa)) { ca => P.map(ca)(f) }) 35 | } 36 | 37 | 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] 38 | 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] 39 | 40 | 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] 41 | 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] 42 | 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] 43 | 44 | 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] { 45 | 46 | def point[A](a: A): CC[A] = NAP.pack(Pivot.point(T.point(a))) 47 | 48 | def map[A, B](fa: CC[A])(f: A => B): CC[B] = 49 | NAP.pack(Pivot.map(NAP.unpack(fa)) { ta => T.map(ta)(f) }) 50 | } 51 | 52 | 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] 53 | 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] 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/scala/emm/effects/MapperBinder.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package effects 3 | 4 | import shims.Monad 5 | 6 | private[emm] object MapperBinder { 7 | 8 | implicit def monad[F <: Effects](implicit FB: Binder[F], FM: Mapper[F]): Monad[F#Point] = new Monad[F#Point] { 9 | def point[A](a: A): F#Point[A] = FM.point(a) 10 | override def map[A, B](fa: F#Point[A])(f: A => B): F#Point[B] = FM.map(fa)(f) 11 | def flatMap[A, B](fa: F#Point[A])(f: A => F#Point[B]): F#Point[B] = FB.bind(fa)(f) 12 | } 13 | } -------------------------------------------------------------------------------- /core/src/main/scala/emm/effects/Traverser.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package effects 3 | 4 | import shims.{Applicative, FlatMap, Functor, Monad, Traverse} 5 | import scala.annotation.implicitNotFound 6 | 7 | import properties._ 8 | 9 | trait Traverser[C <: Effects] { 10 | type CC[A] = C#Point[A] 11 | 12 | def traverse[G[_]: Applicative, A, B](ca: CC[A])(f: A => G[B]): G[CC[B]] 13 | } 14 | 15 | object Traverser { 16 | 17 | implicit def base: Traverser[Base] = new Traverser[Base] { 18 | def traverse[G[_]: Applicative, A, B](fa: A)(f: A => G[B]): G[B] = f(fa) 19 | } 20 | 21 | implicit def corecurse1[F[_], C <: Effects](implicit C: Traverser[C], NN: NonNested[C], F: Traverse[F]): Traverser[F |: C] = new Traverser[F |: C] { 22 | 23 | def traverse[G[_]: Applicative, A, B](fca: CC[A])(f: A => G[B]): G[CC[B]] = { 24 | val back = F.traverse(NN.unpack(fca)) { ca => 25 | C.traverse(ca)(f) 26 | } 27 | 28 | implicitly[Applicative[G]].map(back) { fca2 => NN.pack(fca2) } 29 | } 30 | } 31 | 32 | 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] 33 | 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] 34 | 35 | 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] 36 | 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] 37 | 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] 38 | 39 | 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] { 40 | 41 | def traverse[G[_]: Applicative, A, B](fca: CC[A])(f: A => G[B]): G[CC[B]] = { 42 | val back = Pivot.traverse(NAP.unpack(fca)) { ca => 43 | T.traverse(ca)(f) 44 | } 45 | 46 | implicitly[Applicative[G]].map(back) { fca2 => NAP.pack(fca2) } 47 | } 48 | } 49 | 50 | 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] 51 | 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] 52 | } -------------------------------------------------------------------------------- /core/src/main/scala/emm/effects/Wrapper.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package effects 3 | 4 | import shims.{Applicative, FlatMap, Functor, Monad, Traverse} 5 | import scala.annotation.implicitNotFound 6 | 7 | import properties._ 8 | 9 | @implicitNotFound("could not infer effect stack ${C} from type ${E}") 10 | trait Wrapper[E, C <: Effects] { 11 | type A 12 | type CC[A] = C#Point[A] 13 | 14 | def apply(e: E): CC[A] 15 | } 16 | 17 | trait WrapperLowPriorityImplicits1 { 18 | 19 | implicit def head[A0]: Wrapper.Aux[A0, Base, A0] = new Wrapper[A0, Base] { 20 | type A = A0 21 | 22 | def apply(a: A) = a 23 | } 24 | } 25 | 26 | // not really sure why these functions in particular need to be moved down 27 | trait WrapperLowPriorityImplicits2 extends WrapperLowPriorityImplicits1 { 28 | 29 | def corecurse1[F[_], E, C <: Effects, A0](implicit W: Wrapper.Aux[E, C, A0]): Wrapper.Aux[F[E], F |: C, A0] 30 | 31 | 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] 32 | 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] 33 | } 34 | 35 | object Wrapper extends WrapperLowPriorityImplicits2 { 36 | type Aux[E, C <: Effects, A0] = Wrapper[E, C] { type A = A0 } 37 | 38 | 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] { 39 | type A = A0 40 | 41 | def apply(fe: F[E]): CC[A] = 42 | fe.asInstanceOf[CC[A]] // already proven equivalent; actual evaluation requires a Functor 43 | } 44 | 45 | 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] 46 | 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] 47 | 48 | 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] 49 | } -------------------------------------------------------------------------------- /core/src/main/scala/emm/package.scala: -------------------------------------------------------------------------------- 1 | package object emm { 2 | import effects._ 3 | 4 | implicit class Syntax[A](val a: A) extends AnyVal { 5 | def pointM[C <: Effects](implicit M: Mapper[C]): Emm[C, A] = Emm.point[C, A](a) 6 | def liftM[C <: Effects](implicit L: Lifter[A, C]): Emm[C, L.Out] = Emm.lift[C, A](a) 7 | def wrapM[C <: Effects](implicit W: Wrapper[A, C]): Emm[C, W.A] = Emm.wrap[C, A](a) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/scala/emm/permute.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | 3 | sealed trait Permute2[F[_, _], G[_, _]] 4 | 5 | object Permute2 { 6 | implicit def identity[F[_, _]]: Permute2[F, F] = new Permute2[F, F] {} 7 | implicit def flip[F[_, _]]: Permute2[F, λ[(A, B) => F[B, A]]] = new Permute2[F, λ[(A, B) => F[B, A]]] {} 8 | } 9 | 10 | sealed trait Permute3[F[_, _, _], G[_, _, _]] 11 | 12 | object Permute3 { 13 | implicit def abc[F[_, _, _]]: Permute3[F, F] = new Permute3[F, F] {} 14 | implicit def acb[F[_, _, _]]: Permute3[F, λ[(A, B, C) => F[A, C, B]]] = new Permute3[F, λ[(A, B, C) => F[A, C, B]]] {} 15 | implicit def bac[F[_, _, _]]: Permute3[F, λ[(A, B, C) => F[B, A, C]]] = new Permute3[F, λ[(A, B, C) => F[B, A, C]]] {} 16 | implicit def bca[F[_, _, _]]: Permute3[F, λ[(A, B, C) => F[B, C, A]]] = new Permute3[F, λ[(A, B, C) => F[B, C, A]]] {} 17 | implicit def cab[F[_, _, _]]: Permute3[F, λ[(A, B, C) => F[C, A, B]]] = new Permute3[F, λ[(A, B, C) => F[C, A, B]]] {} 18 | implicit def cba[F[_, _, _]]: Permute3[F, λ[(A, B, C) => F[C, B, A]]] = new Permute3[F, λ[(A, B, C) => F[C, B, A]]] {} 19 | } 20 | 21 | // for reasons of sanity, we're only going to support left-biased higher-order type constructors (for now) 22 | sealed trait PermuteH2[F[_[_], _, _], G[_[_], _, _]] 23 | 24 | object PermuteH2 { 25 | implicit def identity[F[_[_], _, _]]: PermuteH2[F, F] = new PermuteH2[F, F] {} 26 | implicit def flip[F[_[_], _, _]]: PermuteH2[F, λ[(G[_], A, B) => F[G, B, A]]] = new PermuteH2[F, λ[(G[_], A, B) => F[G, B, A]]] {} 27 | } 28 | 29 | // this case gets us scalaz's State, which is all I really care about 30 | sealed trait PermuteH3[F[_[_], _, _, _], G[_[_], _, _, _]] 31 | 32 | object PermuteH3 { 33 | implicit def abc[F[_[_], _, _, _]]: PermuteH3[F, F] = new PermuteH3[F, F] {} 34 | 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]]] {} 35 | 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]]] {} 36 | 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]]] {} 37 | 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]]] {} 38 | 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]]] {} 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/scala/emm/properties/NestedAtPoint.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package properties 3 | 4 | /** 5 | * The property of Effects which contain at least one -|: case, partitioning into a front and tail, where the tail 6 | * is NonNested and the front is unconstrained. 7 | */ 8 | sealed trait NestedAtPoint[C <: Effects, Pivot[_[_], _], Front <: Effects, Tail <: Effects] { 9 | def NN: NonNested[Tail] 10 | 11 | def pack[A](cc: Pivot[Front#Point, Tail#Point[A]]): C#Point[A] 12 | def unpack[A](cc: C#Point[A]): Pivot[Front#Point, Tail#Point[A]] 13 | } 14 | 15 | object NestedAtPoint { 16 | 17 | implicit def split1[Pivot[_[_], _], C <: Effects](implicit C: NonNested[C]): NestedAtPoint[Pivot -|: C, Pivot, Base, C] = new NestedAtPoint[Pivot -|: C, Pivot, Base, C] { 18 | def NN = C 19 | 20 | def pack[A](cc: Pivot[λ[X => X], C#Point[A]]): (Pivot -|: C)#Point[A] = cc.asInstanceOf[(Pivot -|: C)#Point[A]] 21 | def unpack[A](cc: (Pivot -|: C)#Point[A]): Pivot[λ[X => X], C#Point[A]] = cc.asInstanceOf[Pivot[λ[X => X], C#Point[A]]] 22 | } 23 | 24 | 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] 25 | 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] 26 | 27 | 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] { 28 | def NN = C.NN 29 | 30 | def pack[A](cc: Pivot[(C2.F |: F)#Point, T#Point[A]]): C2#Point[A] = cc.asInstanceOf[C2#Point[A]] 31 | 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]]] 32 | } 33 | 34 | 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] 35 | 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] 36 | 37 | // these cases are currently disabled since we have no present use for them and they slow down compilation by roughly 83% 38 | /*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] { 39 | def NN = C.NN 40 | 41 | def pack[A](cc: Pivot[(E.Pivot -|: F)#Point, T#Point[A]]): C2#Point[A] = cc.asInstanceOf[C2#Point[A]] 42 | 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]]] 43 | } 44 | 45 | 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] { 46 | def NN = C.NN 47 | 48 | def pack[A](cc: Pivot[(E.Pivot -|: F)#Point, R, T#Point[A]]): C2#Point[A] = cc.asInstanceOf[C2#Point[A]] 49 | 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]]] 50 | } 51 | 52 | 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] { 53 | def NN = C.NN 54 | 55 | def pack[A](cc: Pivot[(E.Pivot -|: F)#Point, Q, R, T#Point[A]]): C2#Point[A] = cc.asInstanceOf[C2#Point[A]] 56 | 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]]] 57 | }*/ 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/scala/emm/properties/NonNested.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package properties 3 | 4 | /** 5 | * The property of Effects which do not contain a -|: case 6 | */ 7 | sealed trait NonNested[C <: Effects] { 8 | def pack[CC[_], A](cc: CC[C#Point[A]]): C#Build[CC, A] 9 | def unpack[CC[_], A](cc: C#Build[CC, A]): CC[C#Point[A]] 10 | } 11 | 12 | object NonNested { 13 | 14 | implicit def base: NonNested[Base] = new NonNested[Base] { 15 | def pack[CC[_], A](cc: CC[A]) = cc 16 | def unpack[CC[_], A](cc: CC[A]) = cc 17 | } 18 | 19 | implicit def corecurse[C <: Effects, C2 <: Effects](implicit C2: BarExtract[C, C2], C: NonNested[C]): NonNested[C2] = new NonNested[C2] { 20 | def pack[CC[_], A](cc: CC[C2#Point[A]]) = cc.asInstanceOf[C2#Build[CC, A]] 21 | def unpack[CC[_], A](cc: C2#Build[CC, A]) = cc.asInstanceOf[CC[C2#Point[A]]] 22 | } 23 | } -------------------------------------------------------------------------------- /core/src/main/scala/emm/properties/extract.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package properties 3 | 4 | sealed trait BarExtract[T <: Effects, C <: Effects] { 5 | type F[_] 6 | } 7 | 8 | object BarExtract { 9 | type Aux[T <: Effects, C <: Effects, F0[_]] = BarExtract[T, C] { type F[A] = F0[A] } 10 | 11 | implicit def extract1[F0[_], T <: Effects]: BarExtract.Aux[T, F0 |: T, F0] = new BarExtract[T, F0 |: T] { 12 | type F[A] = F0[A] 13 | } 14 | 15 | 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] 16 | 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] 17 | 18 | implicit def extractH1[F0[_[_], _], G[_], T <: Effects]: BarExtract.Aux[T, F0[G, ?] |: T, F0[G, ?]] = extract1[F0[G, ?], T] 19 | 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] 20 | 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] 21 | } 22 | 23 | sealed trait PivotExtract[T <: Effects, C <: Effects] { 24 | type Pivot[_[_], _] 25 | } 26 | 27 | object PivotExtract { 28 | type Aux[T <: Effects, C <: Effects, Pivot0[_[_], _]] = PivotExtract[T, C] { type Pivot[F[_], A] = Pivot0[F, A] } 29 | 30 | implicit def extract1[Pivot0[_[_], _], T <: Effects]: PivotExtract.Aux[T, Pivot0 -|: T, Pivot0] = new PivotExtract[T, Pivot0 -|: T] { 31 | type Pivot[F[_], A] = Pivot0[F, A] 32 | } 33 | 34 | implicit def extract2[Pivot0[_[_], _, _], Z, T <: Effects]: PivotExtract.Aux[T, Pivot0[?[_], Z, ?] -|: T, Pivot0[?[_], Z, ?]] = extract1[Pivot0[?[_], Z, ?], T] 35 | 36 | implicit def extract3[Pivot0[_[_], _, _, _], Y, Z, T <: Effects]: PivotExtract.Aux[T, Pivot0[?[_], Y, Z, ?] -|: T, Pivot0[?[_], Y, Z, ?]] = extract1[Pivot0[?[_], Y, Z, ?], T] 37 | } -------------------------------------------------------------------------------- /project/Build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | 3 | object EmmBuild extends Build { 4 | val shimsVersion = settingKey[String]("Shims version shared between projects") 5 | } -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.9 -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.5") 2 | 3 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "0.5.0") 4 | -------------------------------------------------------------------------------- /scalaz/src/main/scala/emm/compat/scalaz.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | package compat 3 | 4 | object scalaz extends shims.Implicits -------------------------------------------------------------------------------- /scalaz/src/test/scala/emm/ScalazSpecs.scala: -------------------------------------------------------------------------------- 1 | package emm 2 | 3 | import emm.compat.scalaz._ 4 | 5 | import org.specs2.mutable._ 6 | 7 | import scalaz._ 8 | import scalaz.std.option._ 9 | import scalaz.std.list._ 10 | 11 | object ScalazSpecs extends Specification { 12 | 13 | "state stacks" should { 14 | "implement bind" in { 15 | "inner" >> { 16 | type E = Option |: StateT[?[_], String, ?] -|: Base 17 | 18 | (42.pointM[E] flatMap { _ => "foo".pointM[E] }).run.eval("blah") must beSome("foo") 19 | } 20 | 21 | "mid" >> { 22 | type E = List |: StateT[?[_], String, ?] -|: Option |: Base 23 | 24 | (42.pointM[E] flatMap { _ => "foo".pointM[E] }).run.eval("blah") mustEqual List(Some("foo")) 25 | } 26 | } 27 | } 28 | 29 | "kleisli stacks" should { 30 | "implement bind" in { 31 | "inner" >> { 32 | type E = Option |: Kleisli[?[_], String, ?] -|: Base 33 | 34 | (42.pointM[E] flatMap { _ => "foo".pointM[E] }).run.run("blah") must beSome("foo") 35 | } 36 | 37 | "mid" >> { 38 | type E = List |: Kleisli[?[_], String, ?] -|: Option |: Base 39 | 40 | (42.pointM[E] flatMap { _ => "foo".pointM[E] }).run.run("blah") mustEqual List(Some("foo")) 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /scalaz71/build.sbt: -------------------------------------------------------------------------------- 1 | name := "emm-scalaz-71" 2 | 3 | libraryDependencies += "com.codecommit" %% "shims-scalaz-71" % shimsVersion.value -------------------------------------------------------------------------------- /scalaz71/src: -------------------------------------------------------------------------------- 1 | ../scalaz/src -------------------------------------------------------------------------------- /scalaz72/build.sbt: -------------------------------------------------------------------------------- 1 | name := "emm-scalaz-72" 2 | 3 | libraryDependencies += "com.codecommit" %% "shims-scalaz-72" % shimsVersion.value -------------------------------------------------------------------------------- /scalaz72/src: -------------------------------------------------------------------------------- 1 | ../scalaz/src --------------------------------------------------------------------------------