# Monad classes For now, consider the following monad transformers together with base Identity monad: ```haskell class MonadTrans t where lift :: m a -> t m a newtype ReaderT e m a = ReaderT { runReaderT :: e -> m a } instance Functor f => Functor (ReaderT e f) instance Applicative f => Applicative (ReaderT e f) instance Monad m => Monad (ReaderT e m) instance MonadTrans (ReaderT e) newtype WriterT w m a = WriterT { runWriterT :: m (w, a) } instance Functor f => Functor (WriterT w f) instance (Monoid w, Applicative f) => Applicative (WriterT w f) instance (Monoid w, Monad m) => Monad (WriterT w m) instance Monoid w => MonadTrans (WriterT w) newtype Identity a = Identity { runIdentity :: a } instance Functor Identity instance Applicative Identity instance Monad Identity type MyMonad = ReaderT Int (WriterT String Identity) ``` Using transformers, we can compose several effects together, but if you try to write a simple program with them, it will look like the following: ```haskell myProgram :: MyMonad Int myProgram = do env <- ReaderT (\env -> return env) lift (WriterT (return (show env, ()))) return env ``` See how littered this is with different kinds of piping: `return`s, `lift`s, monad constructors etc. Using some auxiliary functions, program becomes a lot cleaner: ```haskell ask0 :: Monad m => ReaderT e m e ask0 = ReaderT return write0 :: (Monoid w, Monad m) => w -> WriterT w m () write0 w = WriterT (return (w, ())) myProgram' :: MyMonad Int myProgram' = do env <- ask0 lift $ write0 (show env) return env ``` ```haskell newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) } instance Functor f => Functor (MaybeT f) instance Applicative f => Applicative (MaybeT f) instance Monad f => Monad (MaybeT f) instance MonadTrans MaybeT type ExtMonad a = MaybeT MyMonad a exit0 :: Monad m => MaybeT m a exit0 = MaybeT (return Nothing) divCtx :: Int -> ExtMonad Int divCtx x = do y <- lift myProgram if y == 0 then exit0 else return (x `div` y) divRed :: Int -> MaybeT (ReaderT Int Identity) Int divRed x = do y <- lift ask0 if y == 0 then exit0 else return (x `div` y) -- use :: ExtMonad Int -- use = divRed 5 ``` ```haskell class Monad m => MonadWriterProto w m where writeProto :: (a, w) -> m a -- effectively same as w -> m () unfoldWriteProto :: m a -> m (a, w) -- ?? -- effectively same as m a -> m w class Monad m => MonadReaderProto e m where readProto :: (e -> a) -> m a readProto f = fmap f askProto askProto :: m e unfold :: m a -> (e -> m a) localProto :: (e -> e) -> m a -> m a localProto f x = do e <- askProto unfold x (f e) ``` ```haskell class Monad m => MonadWriter w m where write :: w -> m () instance (Monad m, Monoid w) => MonadWriter w (WriterT w m) where write = write0 instance MonadWriter w m => MonadWriter w (ReaderT e m) where write = lift . write instance MonadWriter w m => MonadWriter w (MaybeT m) where write = lift . write class Monad m => MonadReader e m where ask :: m e local :: (e -> e) -> m a -> m a instance MonadReader e ((->) e) where ask = id local f g e = g (f e) instance Monad m => MonadReader e (ReaderT e m) where ask = ask0 local f = undefined class Monad m => MonadMaybe m where exit :: m a instance MonadMaybe Maybe where exit = Nothing instance Monad m => MonadMaybe (MaybeT m) where exit = exit0 ``` ```haskell myProgram'' :: (MonadReader Int m, MonadWriter String m) => m Int myProgram'' = do env <- ask write (show env) return env ``` TAGLESS FINAL