[Haskell-cafe] hoisting as well as lifting and returning

Yitzchak Gale gale at sefer.org
Sun Mar 23 16:27:44 EDT 2008


Matthew Pocock wrote:
>  I've been using monad transformers for the first time in anger this week.

I hope your enjoyment in using monads has helped your anger
to subside. :)

>  ...there's an operation that I keep defining over
>  and over again...
>  hoistList :: (Monad m) => [a] -> ListT m a
>  hoistMaybe :: (Monad m) => Maybe a -> MaybeT m a
>  do vInMFT <- hoist vInMF
>  Perhaps 'hoist' already exists and I'm re-inventing the wheel?

You are correct. This is a fundamental operation. It exists
for just about every monad, but in a haphazard and
inconsistent way. In my opinion, its type needs to be
more polymorphic in a slightly different direction than what
you are suggesting.

Here's a sampling of what we have now:

State :: (s -> (a, s)) -> State s a
StateT . return :: Monad m => (s -> (a, s)) -> StateT s m a
liftList :: Monad m => [a] -> ListT m a -- ListT_Done_Right
ErrorT . return :: Monad m => Either e a -> ErrorT e a

You get the picture. Yes, it's a bit of a mess.

A general "hoist" function that would work for
disparate kinds of monads would require yet
an additional parameter to the MonadFoo class
for each monad. Or an additional MonadHoist
type class. (Or whatever the corresponding
additional complexity will be when we move to
associated types.) This would be needed to specify
what the "underlying structure" is that needs to be
hoisted from. I'm not sure that kind of polymorphism
would be worth the additional complexity - unless
someone can suggest a more beautiful way to capture
this generality. Otherwise, I think it would be enough
to have a "hoist" function for each monad, based on
the name of the monad.

What I do sorely feel the need for is a "hoist" for each pair
of base/transformer monads: i.e., polymorphic monad
constructors.

So, for example, if we had

mkState :: (st -> (a, st)) -> m a

as a member of the MonadState st m class,
then it would be so much easier to write functions

f :: MonadState st m => ...

that could be used without having to refactor it every time
the monad stack changes. In general, each monad
Foo would have a MonadFoo class (even the monads
that don't have one yet) containing (at least) a mkFoo
method that lifts the "underlying structure" polymorphically
either to Foo or to FooT.

btw, a variation on this is to provide only a "hoist"
or "mkFoo" for the transformer version of the monad, and
then use only transformers, basing every monad stack
at the Identity monad. This is what Iavor Diatchki does in
his monadlib library. I don't particularly like that approach,
though. In the most common simple case, I like being
able to specify whether my monad is a transformer or
not.

Regards,
Yitz


More information about the Haskell-Cafe mailing list