{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances,
UndecidableInstances #-}
module Types where
import Control.Monad.Writer
-- Exam question:
newtype MaybeT m a = MaybeT {runMaybeT :: m (Maybe a)}
bindMT :: (Monad m) => MaybeT m a -> (a -> MaybeT m b) -> MaybeT m b
x `bindMT` f = MaybeT $ runMaybeT x >>=
maybe (return Nothing)
(runMaybeT . f)
returnMT :: (Monad m) => a -> MaybeT m a
returnMT a = MaybeT $ return (Just a)
failMT :: (Monad m) => t -> MaybeT m a
failMT _ = MaybeT $ return Nothing
instance (Monad m) => Monad (MaybeT m) where
return = returnMT
(>>=) = bindMT
fail = failMT
problem :: MonadWriter [String] m => m Int
problem = do
tell ["I fail"]
fail "oops"
return 1738
error "hej" -- not part of the exam question
type A = WriterT [String] Maybe
type B = MaybeT (Writer [String])
a :: A Int
a = problem
b :: B Int
b = problem
-- What do |runWriterT a| and |runWriter (runMaybeT b)| evaluate to?
-- Explain.
testa = runWriterT a
testb = runWriter (runMaybeT b)
-- For both tests, the call to return has no effect, because the call
-- to fail makes the computation fail before it gets to return.
{-
A a = WriterT [String] Maybe a ~= Maybe (a, [String])
With this type, fail will give Nothing, and only is non-failing cases
will there be a [String] to present. (No good for logging!)
testa == Nothing
-}
{-
type B a = MaybeT (Writer [String]) a ~= Writer [String] (Maybe a)
~= (Maybe a, [String])
With this type, there will always be a String as the second component
of the pair, even when fail gives a Nothing as the first component.
testb == (Nothing,["I fail"])
-}
instance (Monoid w, MonadWriter w m) => MonadWriter w (MaybeT m) where
tell = tellMT
listen = listenMT
pass = error "pass is not part of the exam question"
tellMT :: MonadWriter w m => w -> MaybeT m ()
tellMT w = MaybeT $ tellMT1 w
-- I'm adding type annotations just to clarify a bit.
tellMT1 :: MonadWriter w m => w -> m (Maybe ())
tellMT1 w = tell w >> return (Just ())
listenMT :: MonadWriter w m => MaybeT m a -> MaybeT m (a, w)
listenMT (MaybeT mMa) = MaybeT $ listenMT1 mMa
listenMT1 :: MonadWriter w m => m (Maybe a) -> m (Maybe (a, w))
listenMT1 mma = do (ma, w) <- listen mma
return $ case ma of
Nothing -> Nothing
Just a -> Just (a, w)
----------------------------------------------------------------
main = do
print $ testa == Nothing
print $ testb == (Nothing,["I fail"])