Search code examples
haskelliomonadsstate-monad

A monad of tuples (monad, state)?


I am trying to write a Haskell "number-guessing" game program, using Monads, but I am stucked:
I tried the simple state monad:

data SM a = SMN (S -> (a, S))
instance Monad SM where
 SMN c1 >>= fc2 = SMN (\s0 -> let (r, s1) = c1 s0 in
                                let SMN c2 = fc2 r in
                                  c2 s1)

And I need to perform the IO tasks on the "IO-side" of the tuple (a, S), that is, I tried doing something like:

SMN c1 >>= fc2 = SMN (\s0 -> let (r, s1) = c1 s0 in
                               let SMN c2 = fc2 r in
                                 let (r1, s2) = c2 s1 in
                                   let r2 = r1 >>= (\_ -> r) in
                                     (r2, s2))

In short, the bind operator I would like to define is the same as the original state monad, except that we bind r1 and the constant function that takes an argument to r (so that the two actions are chained together). But the ghc tells me that a is a rigid type variable... What does that mean? I cannot use another bind operator inside one bind operator?
If so, then is there a way to implement such a bind operator? How?
As I am new to Haskell(I think I might have had a notational error concerning the function

\_ -> r

), any opinion and reference are welcomed, thanks in advance.
P.S. I used different notations for the data type SM and the type constructor SMN, so as to differentiate them.


Solution

  • The type of (>>=) is:

    Monad m => m a -> (a -> m b) -> m b
    

    Since you are writing an instance for SM, the type of bind in your instance is therefore

    SM a -> (a -> SM b) -> SM b
    

    Notice that both a and b are completely unrestricted type variables. That means whatever implementation you give must work no matter what types I choose to put in there. In particular, I could choose, say, Int for both a and b:

    SM Int -> (Int -> SM Int) -> SM Int
    

    And now it is clear why your implementation is no good: it will attempt to treat an Int as if it were a monadic action and call (>>=) on it.

    If you want to be able to do monadic actions inside your bind, you will have to talk about the monad in your type somehow; for example, one standard way is to define

    data SMT m a = SMT (S -> m (a, S))
    

    and give an instance like:

    instance Monad m => Monad (SMT m) where -- ...
    

    The normal SM can then be recovered, if you like, by using the Identity monad as the nested monad.