I've been using the Haxl monad (described here: http://www.reddit.com/r/haskell/comments/1le4y5/the_haxl_project_at_facebook_slides_from_my_talk), which has the interesting feature that <*> for its Applicative instance isn't the same as ap from Control.Monad. This is a key feature that allows it to do concurrent computations without blocking. For example, if hf and ha are long computations, then
let hf :: Haxl (a -> b) = ...
ha :: Haxl a = ...
in do
f <- hf
a <- ha
return (f a)
will do them sequentially, while
hf <*> ha
will do them in parallel and then combine the results.
I would like to be able to run computations in MaybeT Haxl, but the problem is that the Applicative instance for MaybeT m in the transformers package uses monadic bind:
instance (Functor m, Monad m) => Applicative (MaybeT m) where
pure = return
(<*>) = ap
Where ap = liftM2 id is from Control.Monad. This makes
let hmf :: MaybeT Haxl (a -> b) = ...
hma :: MaybeT Haxl a = ...
in hmf <*> hma
run sequentially. It seems like a better instance would be more like
instance (Applicative m) => Applicative (MaybeT m) where
pure = MaybeT . pure . Just
MaybeT f <*> MaybeT x = MaybeT $ (<*>) <$> f <*> x
(Here, (<*>) on the right-hand side is for the Maybe monad, while the non-parenthesized <*> on the right-hand side is for m.) Note that the context is different -- the above instance assumes only Applicative m, while the instance in transformers assumes Functor m, Monad m.
My main question is practical: what should I do about this? Should I roll my own MaybeT monad transformer? Is there some way to get around the "Duplicate instance declarations" complaint that ghc gives me if I try to write the above?
I'd also like to know: is the current setup a design flaw in the transformers package? If not, why not?
The trick is that (unlike monads) applicative functors are composable, so you don't need (applicative) transformers such as MaybeT. Instead, you can use Compose to combine two applicative functors together:
import Control.Applicative
import Data.Functor.Compose
type HaxlM = Compose Haxl Maybe
-- if you prefer to have a function for constructing values:
haxlM :: Haxl (Maybe a) -> HaxlM a
haxlM = Compose
The composition is always a proper instance of Applicative and use only the Applicative instance of their components. For example:
test = getZipList . getCompose
$ (+) <$> Compose (ZipList [Just 1, Nothing, Just 3])
<*> Compose (ZipList [Nothing, Just 20, Just 30])
produces [Nothing,Nothing,Just 33].
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With