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 useComposeto combine two applicative functors together:The composition is always a proper instance of
Applicativeand use only theApplicativeinstance of their components. For example:produces
[Nothing,Nothing,Just 33].