
J. Garrett Morris wrote:
Again, from the earlier example, I'm not sure how typing:
apply :: (MonadCont m, MonadState Blargh m, MonadError Fzzt m, MonadIO m) => Handle -> Attribute a -> m a
is simpler than
apply :: Handle -> Attribute a -> m a
Well, no, but it is at least no worse than apply :: Handle -> Attribute a -> ContT (StateT Blargh (ErrorT Fzzt IO)) a I find that in general, many functions do not need all of the capabilities. If they do, you can alias that also: class (MonadCont m, MonadState Blargh m, MonadError Fzzt m, MonadIO m) => MyContext m instance MyContext (ContT (StateT Blargh (ErrorT Fzzt IO))) ... apply :: MyContext m => Handle -> Attribute a -> m a
2. Use a type alias for the monad stack.
At least as of 6.4.2, GHC printed the expanded types, not the aliases, in error messages.
Hmm, I'm not sure. I use a more recent GHC. I know I have seen type aliases in error messages, but I am not certain that they are always used.
(There are other big advantages of both of these.) Those being?
OK, let's see... You often need to make changes to the monad stack - add or remove capabilities, reorder, etc. This way, you only change the type in one place, and only fix functions that use the particular capabilities that were changed. The usual advantages of polymorphism apply - gives separation of concerns, encourages reuse, eases maintenance and testing, better expresses the meaning of the function by not mentioning unneeded details. Monad transformers are like Lego blocks. I find that almost always, different parts of a system need to use different combinations of common monad stack fragments, assembled in different ways. Polymorphism makes that a lot easier to do, and results in functions that are much more readable. By the way, are you really doing CPS? If you are only using ContT to get short-circuiting, you could probably also simplify things by using ExitT instead: http://www.haskell.org/haskellwiki/New_monads#MonadExit Regards, Yitz