The naming of Reader
, Writer
and State
is not very intuitive. For starters, a Reader
is a function, a Writer
is a tuple and a State
is a function that returns a tuple. The magic lies in the implementation of Reader
, Writer
and State
as monads.
- The
Reader
function binds to an operator that "reads" both the function's argument and its return value. - The
Writer
tuple is a state-value pair that binds to a function that takes the value element and creates a new state-value pair. The implementation of>>=
"writes" the new state element to the original state element. - The
State
function takes a state and returns a state-value pair. It binds to an operator that "reads" both the new state element and the new value element.
Reader
Since a Reader
is a mere function, its type is basically ->
. The following two type signatures are equivalent:
a -> b
(->) a b
For the sake of readability, we will define a type synonym R
for ->
. The next two type signatures are equivalent to the former two:
a `R` b
R a b
{-# LANGUAGE TypeSynonymInstances #-}
import Prelude hiding (Monad (..), (.))
x.f = f(x)
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
-- show
type R = (->)
instance Monad (R domain) where
return(x) = \(_) -> x
g >>= f = \(x) -> g(x).f!(x)
where (!) = ($)
reader = (*3) >>= (-)
main = print(reader(42))
-- /show
NOTE: One may be tempted to simply write g(x).f(x)
instead of g(x).f!(x)
. This would still yield a consistent Reader
(with the arguments of f
flipped), yet it would be incompatible with the signature m a -> (a -> m b) -> m b
of >>=
.
Writer
Since a Writer
is a mere tuple, its type is basically (,)
. ((a, b)
is syntactic sugar for (,) a b
.) For the sake of readability, we will define a type synonym W
for (,)
, making the following type signatures equivalent:
(a, b)
(,) a b
W a b
In order for a state value to be "writeable" it must be a Monoid
, i.e. it must implement
<>
(ormappend
), an operator that appends oneMonoid
to anotherMonoid
andmempty
, an "empty"Monoid
.
{-# LANGUAGE TypeSynonymInstances #-}
import Data.Monoid
import Prelude hiding (Monad (..), (.))
x.f = f(x)
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
-- show
type W = (,)
instance Monoid this => Monad (W this) where
return(x) = (mempty, x)
(this, x) >>= f = (this <> this', x')
where (this', x') = f(x)
write(x) = ("inc " ++ show(x) ++ "! ", x + 1)
main = print $ (mempty, 42) >>= write >>= write >>= write
-- /show
NOTE: We might have come up with a Writer
value-state pair (x, this)
rather than a Writer
state-value pair (this, x)
, but such an implementation would be incompatible with the type signature m a -> (a -> m b) -> m b
of >>=
. Besides, whereas (->)
is already an instance of Monad
implementing the Reader
pattern, (,)
has no implementation as a Monad
instance whatsoever.
State
A State
is combination of Reader
and Writer
. It's a Reader
function that takes a state and returns a Writer
state-value pair.
Let's define a type synonym S this a
for R this (W this a)
, i.e. this -> (this, a)
.
NOTE: Since R
is already an instance of Monad
, and we can't make S
an instances of R
, too. But we would fail to implement a Monad
instance for S this
anyway, because we can't unify S this
, a partial applied type synonym, with a Monad
instance m
. We therefore can't implement >>=
(nor return
), failing to unify the type signatures m a -> (a -> m b) -> m b
and S this a -> (a -> S this b) -> S this b
.
import Data.Monoid
import Prelude hiding ((.), Monad(..))
x.f = f(x)
type R = (->)
type W = (,)
-- show
type S this a = R this (W this a)
-- "instance Monad (S this a) where"
return :: a -> S this a
return(x) = \(this) -> (this, x)
(>>=) :: S this a -> (a -> S this b) -> S this b
start >>= f = \(this) ->
let (this', x') = this.start
in this'.f(x')
start = \(this) -> (this ++ "!", 0)
append(x) = \(this) -> (this ++ ".", length(this) - x)
main = do {
print(start $ "hi");
print(start >>= append $ "hi");
print(start >>= append >>= append $ "hi");
}
-- /show
In the next tutorial we shall reimplement Reader
, Writer
and State
as newtype
instances instead of type synonyms (type
).