Monad Transformers Explained
From HaskellWiki
Below is a short explanation of and motivating example for monad transformers. Comments and aggressive editing welcome.
Thanks to Ryan Ingram and Tommy M McGuire of Haskell-Cafe for their comments on an earlier draft.
Basically it's like making a double, triple, quadruple, ... monad by wrapping around existing monads that provide wanted functionality.
You have an innermost monad (usuallyIdentity
IO
IO ()
forkIO :: IO () -> IO ThreadID
Suppose also that this server has some configuration that (in an imperative program) would be global because client handling threads all need to query it.
data Config = Config Foo Bar Baz
Config -> IO ()
Config
Reader
IO
ReaderT
ReaderT
ReaderT r m a
r
m
a
client_func
client_func :: ReaderT Config IO ()
ask
asks
local
do
p <- asks portport :: Config -> Int
lift
lift $ lift $ lift $ foo
Reader
MonadReader
ReaderT
MonadReader
MonadReader
ask
asks
local
IO
liftIO
h :: Handle
liftIO $ hPutStrLn h "You win" liftIO $ hFlush h
MonadIO
IO
MonadIO
MonadIO
MonadIO
IO
liftIO
lift
IO
client_func
ReaderT Config IO ()
forkIO
IO ()
Reader
runReader :: Reader r a -> r -> a
ReaderT
runReaderT :: ReaderT r m a -> r -> m a
c :: Config
forkIO (runReaderT client_func c)
Will do the trick.
Monad transformers are like onions. At first, they make you cry but then you learn to appreciate them. Like onions, they're also made of layers. Each layer is the functionality of a new monad, you lift monadic functions to get into the inner monads and you have transformerised functions to unwrap each layer. They're also like a present in that regard: in this example we unwrapped the outer wrapping paper to get to the present: an object of typeIO ()
