Monads
Monads can be used to sequence operations.
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
The (>>=) function is called bind. It takes a monadic value and a
function which gives a monadic value and returns a monadic value.
The monad instance for Maybe is as follows:
instance Monad Maybe where
Nothing >>= _ = Nothing
Just x >>= f = f x
Monad laws
-
Left identity -
pure x >> f = f x -
Right identity -
m >>= pure = m -
Associativity -
(m >>= f) >>= gis the same asm >>= (\x -> f x >>= g)
Either
Either is a type similar to Maybe.
data Either e a = Left e | Right a
Instead of just having a Nothing value, it has an error type Left e
which can hold additional information.
instance Functor (Either e) where
fmap f (Left x) = Left x
fmap f (Right y) = Right (f y)
instance Applicative (Either e) where
pure x = Right x
Left x <*> _ = Left x
_ <*> Left y = Left y
Right f <*> Right x = Right (f x)
instance Monad (Either e) where
Left x >>= _ = Left x
Right x >>= f = f x
Reader
Reader is wrapper around the environment functor.
newtype Reader e a = Reader { runReader :: e -> a }
instance Functor (Reader e) where
fmap f (Reader func) = Reader (f . func)
Reader uses the ask function to get a value from a reader.
ask = Reader (\e -> e). The runReader function can be used to
evaluate a Reader.
Writer
The Writer type is a wrapper around a pair of values.
newtype Writer w a = Wr { runWriter :: (w, a) }
instance Monoid w => Monad (Writer w) where
Wr (w, x) >>= f =
let Wr (w', x') = f x
in Wr (w <> w', x')
State
The State monad is a wrapper around functions which have a stateful
component.
newtype State s a = St ( s -> (a, s) )
instance Functor (State s) where
fmap f (St func) = St (f1 . func)
where
f1 (x, s) = (f x, s)
The fmap definition for State takes a function and creates another
function which takes the pair returned by the function in the state,
applies the given function to the first item in the pair and composes it
with the existing state function.
instance Applicative (State s) where
pure x = St (\s -> (x, s))
St sf <*> St sx = St (\s -> let
(f, s') = sf s
(x, s'') = sx s'
in (f x, s''))
The applicative instance takes a State containing a function and one
containing a value. It returns a new State whose function works as
follows:
-
Take
sfrom the new function and callsfwith it, returning a pair containing the function and the resulting stateful component,s' -
Take
s'and apply it tosxto get the pair containing the value and the updated stateful component. -
Return a pair containing the result of applying the function to the value and the final stateful component
s''
instance Monad (State s) where
St func >>= f = St (\s -> let
(x, s') = func s
St func' = f x
in func' s')
This returns a new state whose function is
-
Apply the given
sto the given state to get a value and new stateful component -
Apply the given function to the extracted value to get a new state
-
Apply to the function in the state from the last step to the extracted stateful component,
s'. The result of this is the pair returned by the new state.
The State monad comes with helper functions.
get :: State s s
get = St (\s -> (s, s))
put :: State s ()
put s' = St (\_ -> ( (), s' ))
modify :: (s -> s) -> State s ()
modify f = get >>= \val -> put (f val)
get gets the value from a state, put takes a value and puts it into
the state and modify applies f to the state.
IO
The IO monad allows a program to interact with the real world. IO is opaque, it's not possible to access it's internal working. IO is technically pure, but it takes the real world as its argument. This means although it running a program can have side effects because all of these effects are in the real world it is seen as pure.
Do notation
Consider the following:
stateWithBind :: State String Int
stateWithBind = get >>= \_ -> modify (++ "bind") >>= \_ -> modify (++ "function") >>= \_ -> pure 21
This is hard to read and there seems to be a lot of unnecessary lambda functions. Do notation allows this to be written much cleaner:
stateWithDo :: State String Int
stateWithDo = do
get
modify (++ "bind")
modify (++ "function")
pure 21
It's like each line of a do block ends with >>= \_ -> (except the last
one). Do notation makes the expression much more readable.
Monad transformers
Monad transformers take a monad and add some functionality.
newtype ReaderT e m a = ReaderT { runReaderT :: e -> m a }
ReaderT is a monad transformer which allows a monad to read some
shared environment. There is also WriterT which allows a monad to
collect some logging output.