Search code examples
haskellstatemonadsfunctor

why are the state and reader monads functions while the writer monad is a tuple?


I'm a Haskell newbie, and I think I understand monads and their mechanics (at least for the list, state, maybe, writer and reader monads), but I want to understand why they have been defined the way they have, or why they have to be the way they are, to aid in my intuition in thinking about them.

Specifically, what is it about reading that makes the reader or state monads need to be functions (i.e. \s -> (a,s) ), and not just data like the writer monad (i.e. (w,a) )?

Also, could a writer monad be used as a state monad, where the log is used as a string representation of state, as long as the MonadPlus functionality is not used? Is the monadic function used with writer monads allowed to look at the current log and modify if at will, or is it only the writer monad's bind function that is allowed to look at the log?

Also, why are monads defined in terms of monadic functions that with type a -> m b, instead of with type m a -> mb? What's so natural about a function going from a base type to a monad wrapped type?

Thanks for your answers.


Solution

  • The state and reader monad both depend on the value of whatever state we're interested in. We need to be able to access it otherwise how could we write something like

    foo = do
     i <- get
     return $ if i == 1 then 2 else 3
    

    So our state monad naturally looks like something that takes in a state, does stuff, and produces a new one.

    Likewise with the reader monad, we take in some state, magic, and produce an output (but no new state since it's Reader s a <=> s -> a.

    Now this is very different than the writer monad. In the writer, we don't care about what's been stuck in the state previously, all we want to do is take our current state, and jam some more stuff in there. Because our state is a monoid, we have a guaranteed start state mempty and a way to jam stuff in mappend. This means that we can "run" each computation in an empty context so to speak and just fuse the outputs together and get identical results.

    Ok, now there's about a few questions so I'll go one at a time

    1. If the writer monad had access to the previously written results, then it's going to look like s -> (s, a) which is the state monad :) So yes, whatever you could do with the state monad could be done with this augmented writer.

      In fact both the StateT and WriterT monads have MonadPlus instances so I'm not sure what you're getting at here..

    2. Since there isn't an arbitrary function m a -> a, we need some way to unwrap a computation to view the results ala >>=, and since return :: a -> m a makes it trivial to go the other way, it's more general to go from plain to monadic value. Otherwise we'd just have these useless IO String, STM Int and whatnot that we couldn't depend on for the rest of our computation since fmap won't let us hoist in more side effects.