why are the state and reader monads functions while the writer monad is a tuple?
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
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
andWriterT
monads haveMonadPlus
instances so I'm not sure what you're getting at here..Since there isn't an arbitrary function
m a -> a
, we need some way to unwrap a computation to view the results ala>>=
, and sincereturn :: 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 uselessIO String
,STM Int
and whatnot that we couldn't depend on for the rest of our computation sincefmap
won't let us hoist in more side effects.
They're defined differently because they do different things.
Take the reader monad. Start by thinking about what it means, not about how it works.
A computation in the reader monad is one that depends on an extra piece of information, the reader's "environment". So a Reader Env Int
is an Int
that depends on the environment (of type Env
); if I evaluate it with one environment I'll get one Int
value, and if I evaluate it with a different environment I'll get another Int
value. If I don't have an environment I can't know what value the Reader env Int
is.
Now, what kind of value will give me an Int
if I give it an Env
? A function of type Env -> Int
! So that generalises to e -> a
being a monad for each e
(with a
being the type parameter of the monad; (->) e
if you like the prefix notation).
Now lets think about the meaning of the writer monad. A computation in the writer monad produces a value, but it also produces an extra value "on the side": the "log" value. And when we bind together a series of monadic computations from in the writer monad, the log values will be combined (if we require the log type to be a monoid, then this guarantees log values can be combined with no other knowledge about what they are). So a Writer Log Int
is an Int
that also comes with value of type Log
.
That sounds a lot like simply a pair: (Log, Int)
. And that generalises to (w, a)
being a monad for each w
(with a
being the type parameter of the monad). The monoid constraint on w
that guarantees we can combine the log values also means that we have an obvious starting value (the identity element for the monoid: mempty
), so we don't need to provide anything to get a value out of a value in the writer monad.
The reasoning for the state monad to be s -> (a, s)
is actually pretty much a combination of the above; a State S Int
is an Int
that both depends on an S
value (as the reader depends on the environment) and also produces an S
value, where binding together a sequence of state computations should result in each one "seeing" the state produced by the previous one. A value that depends on a state value is a function of the state value; if the output comes "along with" a new state value then we need a pair.