Hopefully this will be the first in a series of posts about monad transformers. I’m not saying anything new here and there are other resources online that go through this area starting from more basic principles. My goal is to give an overview of transformers and then to explore some of the transformer libraries in later posts.

I’m trying to keep things short and sweet so it’ll be mostly code with a few comments:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
module TransformersPart1 where import Control.Monad.Trans.Maybe import Data.Functor import Text.Read (readMaybe) -- plain old Maybe example (avoiding division by zero) ex1 :: Int -> Int -> Maybe Int ex1 x y | y > 0 = Just (x `div` y) | otherwise = Nothing -- now we have two Monad layers. Maybe and IO. ex2 :: IO (Maybe Int) ex2 = readMaybe <$> getLine -- using the previous example, we now want to map the Int to a String -- we fmap twice; once on the IO and then on the Maybe. ex3 :: IO (Maybe String) ex3 = fmap (fmap show) ex2 -- we can actually do this for any two functors though -- because functors compose ex4 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b) ex4 f a = fmap (fmap f) a -- but when we try to bind we have issues -- we can do it if we know about one of the Monads. ex5 :: Monad m => m (Maybe a) -> (a -> m (Maybe b)) -> m (Maybe b) ex5 a f = a >>= (maybe (return Nothing) f) -- but you can't write bind for any two arbitrary monads ex6 :: (Monad m, Monad n) => m (n a) -> (a -> m (n b)) -> m (n b) ex6 = error "try implementing it" -- With transformers, you have a monad instance for the transformer -- where there's a concrete monad and any other monad on top. Hence -- Monad transformers such as MaybeT, EitherT, etc. The monad instance has to -- be defined with regard to some known, conrete monad + an arbitrary monad. -- the type is `newtype MaybeT m a` so we can use `ex2` to construct one ex7 :: MaybeT IO Int ex7 = MaybeT $ ex2 -- the monad instance for MaybeT lets us to a single bind ex8 :: MaybeT IO Int ex8 = do x <- ex7 return (x + 1) |