Add Applicative instances for MTL types

Bas van Dijk v.dijk.bas at gmail.com
Wed Jan 14 12:16:42 EST 2009


Thanks for the reply.

On Wed, Jan 14, 2009 at 2:50 PM, Ross Paterson <ross at soi.city.ac.uk> wrote:
> The Functor instances could depend on Functor rather than Applicative.

Ok you mean like:

instance Functor m => Functor (ErrorT e m) where
    fmap f = ErrorT . fmap (fmap f) . runErrorT

instance Functor m => Functor (ListT m) where
    fmap f = ListT . fmap (fmap f) . runListT

instance (Functor m) => Functor (ReaderT r m) where
    fmap f = ReaderT . fmap (fmap f) . runReaderT

instance (Functor m, Monoid w) => Functor (WriterT w m) where
    fmap f = WriterT . fmap (\(x, w) -> (f x, w)) . runWriterT

I could update the patch with this or I can create a separate ticket
for it. What do you think?

The latter instance indicates that WriterT should have its inner tuple reversed:

newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }  -->
newtype WriterT w m a = WriterT { runWriterT :: m (w, a) }

Because then we can write the more consistent:

instance (Functor m, Monoid w) => Functor (WriterT w m) where
    fmap f = WriterT . fmap (fmap f) . runWriterT

But this is probably a ticket on it own.

> Even though Applicative is not a superclass of Monad, I think we ought to
> ensure that the instances are compatible.  That is, if an Applicative
> is also a Monad, then we should have pure = return and (<*>) = ap.

Yes, but what if an Applicative isn't a Monad?

We can't have two instances because they overlap:

instance Monad m => Applicative (ErrorT e m) where
    pure  = return
    (<*>) = ap

instance Applicative m => Applicative (ErrorT e m) where
    pure      = ErrorT . pure . pure
    ef <*> ex = ErrorT $ liftA2 (<*>) (runErrorT ef) (runErrorT ex)

I think the latter is more useful because there are more Applicatives
than Monads out there.

> This fails for your ErrorT instance: ap runs the second computation
> only if the first succeeded, while (<*>) runs them both before checking
> for errors.  It needs a Monad constraint (like StateT), though not an
> Error constraint.

But isn't 'runErrorT ex' only evaluated when 'runErrorT ef' returns
'Right f' because of lazy evaluation?

>> * Can we get rid of the Monad and MonadPlus constraints in the
>> Applicative and Alternative instances for StateT and RWST?
>
> I don't think so: you need part of the value generated by the first
> computation, namely the state (inside the f), to construct the second one.
> You can do that in a Monad, but not in an Applicative.

Yes I thought so.

> At Henning Thielemann's request, I've recently put up on hackage a
> restructuring of the mtl into three packages, to provide three different
> interfaces to the same monad transformers:
>
> transformers: a Haskell 98 package with the MonadTrans class, concrete
>        monad transformers, operations and liftings.
> monads-fd: multi-parameter monad classes using functional dependencies,
>        with instances for these transformers.  (Almost backward-compatible
>        with the mtl package.)
> monads-tf: monad classes using type families, with instances for these
>        transformers.
>
> The first one includes Applicative instances like these.

Yes I saw it. Very nice! What is the long term goal of these
libraries? Are they intended to replace mtl one day?


More information about the Libraries mailing list