djspiewak / emm Goto Github PK
View Code? Open in Web Editor NEWA general monad for managing stacking effects
A general monad for managing stacking effects
Hi,
I would really love to use this lib. Have you deployed it on a public repo yet?
cheers,
Romain
Hi,
back in the old days one could create any type constructor C[_]
, then turn it into a functor by saying type MyFunctor[A] = Coyoneda[C, A]
, then wrap it up into a free monad like type MyFreeMonad[A] = Free[MyFunctor, A]
(or even FreeT
), and finally write a custom interpreter (provide something of type C ~> Id
or C ~> Future
and then lift through Coyoneda). This way it was possible to create a completely arbitrary monad with potentially few different interpreters. Is there any new method to do that, or is it required to create the same free monad manually and use it inside the stack (like [Future |: MyFreeMonad |: Option Base]
)? It would be cool if one could avoid the whole Free[Coyoneda[...]]
stuff...
The following code works for Option[?] but doesn't work for Either[Fixed, ?]
import cats._
import cats.implicits._
import emm._
import emm.compat.cats._
import emm.effects.Mapper
object Test {
type Result[T] = Either[String, T]
val applicativeResult = Applicative[Result]
val mapperBase = implicitly[Mapper[Base]]
val mapperOptionBase = implicitly[Mapper[Option |: Base]]
val mapperResultBase = implicitly[Mapper[Result |: Base]] // <-- doesn't compile (14:36)
}
yields error:
Error:(14, 36) could not compute a method for mapping over effect stack emm.|:[de.zalando.artmc.Test.Result,emm.Base]; either a member of the stack lacks an Applicative, or its Applicative instance is ambiguous
val mapperResultBase = implicitly[Mapper[Result |: Base]]
Error:(14, 36) not enough arguments for method implicitly: (implicit e: emm.effects.Mapper[emm.|:[de.zalando.artmc.Test.Result,emm.Base]])emm.effects.Mapper[emm.|:[de.zalando.artmc.Test.Result,emm.Base]].
Unspecified value parameter e.
val mapperResultBase = implicitly[Mapper[Result |: Base]]
The error message isn't correct, as applicative is proven by this line val applicativeResult = Applicative[Result]
so it exists and isn't ambiguous.
I use:
scalaVersion := "2.11.8"
val emmVersion = "0.2.1"
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.8.0")
addCompilerPlugin("com.milessabin" % "si2712fix-plugin" % "1.2.0" cross CrossVersion.full)
It would be useful to have a function on Emm
, transform
, which takes a natural transformation F ~> G
such that F
is in the effect stack, and the resulting Emm
has an effect stack where F
has been replaced with G
.
Right now, it's somewhat difficult to compose functions which return Emm
but with differing (but potentially still compatible) effect stacks. We should have a function which handles this problem. For example:
val e1: Emm[Task |: Base, Int] = ???
val e2: Emm[Option |: Base, String] = ???
val (u1, u2) = Emm.unify(e1, e2)
:t u1 // => Emm[Task |: Option |: Base, Int]
:t u2 // => Emm[Task |: Option |: Base, String]
If we try to encode the idea of using a Traversable constraint to get "composable Monads" in Haskell:
module TraverseMonad where
import Control.Monad (ap, join)
newtype TraverseMonad t f a = TraverseMonad { unTraverseMonad :: t (f a) } deriving (Show, Eq)
instance (Functor t, Functor f) => Functor (TraverseMonad t f) where
fmap f (TraverseMonad tfa) = TraverseMonad $ fmap (fmap f) tfa
instance (Applicative t, Applicative f) => Applicative (TraverseMonad t f) where
pure = TraverseMonad . pure . pure
TraverseMonad tff <*> TraverseMonad tfa = TraverseMonad $ (<*>) <$> tff <*> tfa
instance (Monad t, Monad f, Traversable f) => Monad (TraverseMonad t f) where
return = pure
TraverseMonad tfa >>= f = TraverseMonad $ tfa >>= fmap join . traverse (unTraverseMonad . f)
We'll notice the Functor and Applicative instances are exactly the same as Data.Functor.Compose
.
But when testing the Monad laws with QuickCheck, we can find a broken law:
(<*>) = ap
(i.e. (<*>) = (\m1 m2 -> do { x1 <- m1; x2 <- m2; return (x1 x2) })
)Here's the test, using Maybe
:
{-# LANGUAGE FlexibleContexts #-}
module TraverseMonadCheck where
import TraverseMonad
import Test.QuickCheck
instance Arbitrary (t (f a)) => Arbitrary (TraverseMonad t f a) where
arbitrary = TraverseMonad <$> arbitrary
apProp :: (Eq (m (a, b)), Monad m) => m a -> m b -> Bool
apProp tfa tfb = ((,) <$> tfa <*> tfb) == (((,) <$> tfa) `ap` tfb)
maybeApProp :: TraverseMonad Maybe Maybe Int -> TraverseMonad Maybe Maybe Char -> Bool
maybeApProp = apProp
Which gives the following counter-example:
tfa = TraverseMonad {unTraverseMonad = Just Nothing}
tfb = TraverseMonad {unTraverseMonad = Nothing}
TraverseMonad {unTraverseMonad = Nothing} = TraverseMonad {unTraverseMonad = Just Nothing}
I think the easiest way to resolve this is to create "CommutativeApplicative" and "CommutativeMonad" type-classes and those as constraints. The first would require the law that fmap ($)
is identity on the left hand side of <*>
. I think there'd be a bit more work on the Monad version.
No one knows these things exist! Probably because you would literally need to read the implicit resolution chains to learn anything about them…
It's kinda important to be able to lift into stacks which contain effects in the kleisli category.
How bout it? What do you think?
This might be an interesting function:
val e1: Emm[Task |: List |: Option |: Base, A] = ???
e1.flip[List, Option] // => Emm[Task |: Option |: List |: Base, A]
val e2: Emm[Task |: List |: (String \/ ? ) |: Option |: Base, A] = ???
e2.flip[List, Option] // => Emm[Task |: Option |: (String \/ ?) |: List |: Base, A]
Should be implementable for any useful stack, since we just need to traverse things to flip around.
I've tried many times to try and understand monad transformers, but I still don't get them.
Is there any chance you (or somebody close to this project) could explain what this does without referring to monad transformers? (or scalaz / cats)
Hi Daniel, Shim is really neat. When will you apply it to emm?
I need to depend on emm on a scalaz project :)
_S
Unable to compile this:
import emm._
import emm.compat.cats._
import cats._
import cats.data.ReaderT
import cats.data.ReaderT._
import cats.std.option._
import cats.std.list._
type E = List |: Option |: ReaderT[Id, String, ?] |: Base
val name = Option("x")
name.liftM[E]
When remove ReaderT
from stack it compiles.
There is now
addCompilerPlugin("com.milessabin" % "si2712fix-plugin_2.11.8" % "1.1.0")
So start deleting implicits ;-)
Maybe even depend on the compiler plugin by default?
The first example in README.md does not compile:
[error] /Users/maek/src/emm-test/src/main/scala/EmmTest.scala:25: could not lift scalaz.concurrent.Task[String] into stack EmmTest.E; either EmmTest.E does not contain a constructor of scalaz.concurrent.Task[String], or there is no Functor for a constructor of scalaz.concurrent.Task[String]
[error] first <- readName.liftM[E]
[error] ^
[error] /Users/maek/src/emm-test/src/main/scala/EmmTest.scala:26: could not lift scalaz.concurrent.Task[String] into stack EmmTest.E; either EmmTest.E does not contain a constructor of scalaz.concurrent.Task[String], or there is no Functor for a constructor of scalaz.concurrent.Task[String]
[error] last <- readName.liftM[E]
[error] ^
[error] /Users/maek/src/emm-test/src/main/scala/EmmTest.scala:28: could not lift Option[String] into stack EmmTest.E; either EmmTest.E does not contain a constructor of Option[String], or there is no Functor for a constructor of Option[String]
[error] name <- (if ((first.length * last.length) < 20) Some(s"$first $last") else None).liftM[E]
[error] ^
[error] /Users/maek/src/emm-test/src/main/scala/EmmTest.scala:30: could not lift scalaz.concurrent.Task[Unit] into stack EmmTest.E; either EmmTest.E does not contain a constructor of scalaz.concurrent.Task[Unit], or there is no Functor for a constructor of scalaz.concurrent.Task[Unit]
[error] _ <- log(s"successfully read in $name").liftM[E]
[error] ^
[error] four errors found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 5 s, completed Jan 3, 2016 7:58:51 PM
Live code in scastie: http://scastie.org/14186
Tried to work through the readme tonight which referenced examples that I believe only work with version 0.2, but when looking for version 0.2 the most recent I see is 0.1.
http://mvnrepository.com/artifact/com.codecommit/emm-core_2.11
I thought maybe you forgot to publish them or I needed to use a different repo.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.