13
votes

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?

1
Everyone tends to assume that superclass implementations match subclass implementations exactly. Haxl is violating this unwritten rule and thus can't play nicely with others. Transformers is also assuming you're making Monad transformers, thus its implementation. Applicative "transformers" have much nicer structure.J. Abrahamson
@J.Abrahamson, this is not a rule, written or otherwise. Nor is this the only case where <*> is better, in some significant way, than ap. It seems to me that MaybeT is in the wrong here.dfeuer
After AMP is wide-spread as part of GHC 7.10, we could rewrite Applicative (MaybeT m) definition. currently Applicative isn't a superclass of Monad, so you might break lots of packages. Also I see surprisingly many Foo is instance of Monad but not of Applicative warnings still. Would suggest to write your own MaybeT for now - or maybe make a PR to transformers?.phadej
@dfeuer It tends to be an unwritten rule. People frequently assume you can just switch out do syntax and (<*>) syntax as convenient and pretty. Once AMP lands this will be even stronger, but it'll also be pretty easy to switch out this definition with the Pplicative only one.J. Abrahamson
Actually it's a written rule – it's right there in the Control.Applicative documentationBen Millwood

1 Answers

7
votes

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].