Anatomy of a monad transformer

I'm trying to learn monad transformers, based on the standard Haskell libraries (mtl? transformers? not sure which one came with my download of the Haskell platform - 7.4.1).

What I believe I've noticed is a common structure for each monad transformer definition:

  • base type ('Base')

  • Monad instance
  • transformer type ('BaseT')

  • Monad instance

  • MonadTrans instance

  • MonadIO instance

  • transformer class ('MonadBase')

  • some operations

  • instances for other 'BaseT's

  • So for example, for the Writer monad, there'd be:

  • a Writer datatype/newtype/type, with a Monad instance
  • a WriterT datatype/newtype/type, with Monad, MonadTrans, and MonadIO instances
  • a MonadWriter class, and instances of this class for StateT, ReaderT, IdentityT, ...
  • Is this how monad transformers are organized? Am I missing anything/do I have any incorrect details?

    The motivation for this question is figuring out:

  • what the relationships and differences are between the "BaseT"s and the corresponding "MonadBase"s and "Base"s
  • whether all three are required
  • how MonadTrans is related and what its purpose is

  • mtl package doesn't implement monad transformers. At least WriterT is just reexported from transformers .

    transformers package implements WriterT , which is a monad transformer itself. Writer is just an alias:

    type Writer w = WriterT w Identity
    

    Some libraries can implement Writer separately, but anyway it is just a special case of WriterT . ( Identity is a trivial monad, it doesn't have any additional behavior.)

    MonadTrans allows you to wrap underlying monad into the transformed one. You can live without it, but you will need to perform manual wrapping (see MonadTrans instance definition for WriterT for example how to do it). The only use case where you really need MonadTrans -- when you don't know actual type of transformer.

    MonadWriter is a type class declared in mtl . It's methods ( writer , pass , tell and listen ) are the same as function for WriterT . It allows to wrap (automatically!) WriterT computation through stack of transformers, even if you don't know exact types (and even number!) of transformers in the stack.

    So, WriterT is the only type which is "required".

    For other monad transformers it is the same: BaseT is a transformer, Base is a monad without underlying monad and MonadBase is a type class -- class of all monads, that have BaseT somewhere in transformers stack.

    ADDED:

    You can find great explanation in RWH book

    Here is a basic example:

    import Control.Monad.Trans
    import Control.Monad.Trans.Writer
    import Control.Monad.Trans.Reader hiding (ask)
    
    -- `ask` from transformers
    -- ask :: Monad m => ReaderT r m r
    import qualified Control.Monad.Trans.Reader as TransReader (ask)
    
    -- `ask` from mtl
    -- ask :: MonadReader r m => m r
    import qualified Control.Monad.Reader as MtlReader (ask)
    
    -- Our monad transformer stack:
    -- It supports reading Int and writing String
    type M m a = WriterT String (ReaderT Int m) a
    
    -- Run our monad
    runM :: Monad m => Int -> M m a -> m (a, String)
    runM i action = runReaderT (runWriterT action) i
    
    test :: Monad m => M m Int
    test = do
      tell "hello"
      -- v <- TransReader.ask     -- (I) will not compile
      v1 <- lift TransReader.ask  -- (II) ok
      v2 <- MtlReader.ask         -- (III) ok
      return (v1 + v2)
    
    main :: IO ()
    main = runM 123 test >>= print
    

    Note that (I) will be rejected by compiler (try it to see the error message!). But (II) compiles, thanks to MonadTrans ("explicit lifting"). Thanks to MonadReader , (III) works out of the box ("implicit lifting"). Please read RWH book for explanation how it works.

    (In the example we import ask from two different modules, that is why we need qualified import. Usually you will use only one of them at a time.)

    Also I didn't mean to specifically ask about Writer .

    Not sure I understand... Reader , State and others use the same schema. Replace Writer with State and you will have an explanation for State .

    链接地址: http://www.djcxy.com/p/77364.html

    上一篇: 当一个泛型不会是单子吗?

    下一篇: Monad变压器的解剖