When learning the haskell monad transformer(would call it monadt in the left content), a problem in my mind is why monadT
?
Because from a simple example view, we could make a pale of monad together to do what we want we want to do.
Technially, it's correct, the monadT is just a wrapper. For example, those two cases are equal.
ioOp :: IO String
ioOp = getLine
maybeOp :: Maybe String -> Maybe String
maybeOp a = fmap (++"suffix") a
combinedOpInMonadT :: MaybeT IO String
combinedOpInMonadT =
lift ioOp >>= \input -> MaybeT (pure (maybeOp (Just input)))
-- lift ioOp >>= \input -> MaybeT (pure (maybeOp (Just input)))
combineOpManually :: IO (Maybe String)
combineOpManually =
ioOp >>= \input -> pure $ maybeOp (Just input)
triggerBoth :: IO ()
triggerBoth = do
runMaybeT combinedOpInMonadT >>= print
combineOpManually >>=print
We could also confirm this idea from the source code of MaybeT implementation.
Here I show show a brief but important code snippet here. What't do you find from the code? It is just a wrapper. However,
what's more, you should notice that it(MaybeT m
) also performs as a new monad and have the similar behavior of the maybe
.
-- | A monad transformer which adds Maybe semantics to an existing monad.
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
instance (Functor m) => Functor (MaybeT m) where
fmap f = MaybeT . fmap (fmap f) . runMaybeT
instance (Monad m) => Monad (MaybeT m) where
fail _ = MaybeT (return Nothing)
return = lift . return
x >>= f = MaybeT (runMaybeT x >>= maybe (return Nothing) (runMaybeT . f))
instance (Monad m) => MonadPlus (MaybeT m) where
mzero = MaybeT (return Nothing)
mplus x y = MaybeT $ do v <- runMaybeT x
case v of
Nothing -> runMaybeT y
Just _ -> return v
After knowing this, we should know it's common to compose multiple monads together to support new utilites. However,
haskell doesn't allow us to do that arbitaryly. We should achieve this with the help of monadT.
The important part of monadT i think is it gives the ability to avoid unbox and box efforts. Sometimes as a library, multiple
composition of monad really a lousy thing.
For example , how do you operate a String
inside the value return by the libMethod
? You have no way but unbox by a lot of
helper functions.
libMethod :: A (B (C (D (E String))))
However, benifited from the monadT, we could have a way to treat the A (B (C (D (E
as an another monad, let's call it
NewMonad
here and the function signature becomes this.
libMethod :: NewMonad String
Nice, it's clear and easy to use.
Further more, I'd provide another code snippet for you to better understand the monadT. If you could treat the composition as a new monad, you are more flexiable to write your code and lest disturbing by noisy and sleasy unbox-box.
addPrefix :: String -> String
addPrefix str = "prefix" ++ str
theProblemOfIOMaybeString :: IO ()
theProblemOfIOMaybeString = do
-- not easy to bind addPrefix on the IO (Maybe String)
-- bind directly cannot work!
-- combineOpManually >>= addPrefix >>= print
(combineOpManually >>= \input -> pure $ fmap addPrefix input) >>= print
noTrouble :: IO ()
noTrouble = do
runMaybeT (fmap addPrefix combinedOpInMonadT) >>= print