
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