3
votes

I am building some product monads from Control.Monad.Product package. For reference, the product monad type is:

newtype Product g h a = Product { runProduct :: (g a, h a) } 

Its monad instance is:

instance (Monad g, Monad h) => Monad (Product g h) where
    return a = Product (return a, return a)
    Product (g, h) >>= k = Product (g >>= fst . runProduct . k, h >>= snd . runProduct . k)
    Product (ga, ha) >> Product (gb, hb) = Product (ga >> gb, ha >> hb)

source: http://hackage.haskell.org/packages/archive/monad-products/3.0.1/doc/html/src/Control-Monad-Product.html

Problem I

I build a simple monad that is a product of two State Int Monads, However, when I try to access the underlying state next:

ss :: Product (State Int) (State Int) Int
ss = do 
  let (a,b) = unp $ P.Product (get,get) :: (State Int Int,State Int Int)  
  return 404

You see get just creates another State Int Int, and I am not sure how to actually get the value of the underlying state, how might I do that? Note I could potentially runState a and b to get the underlying value, but this solution doesn't seem very useful since the two states' initial values must be fixed a priori.

Question II.

I would really like to be able to create a product monad over states of different types, ie:

ss2 :: Product (State Int) (State String) ()
ss2 = do 
  let (a,b) = unp $ P.Product (get,get) :: (State Int Int,State Int String)  
  return ()

But I get this type error:

 Couldn't match expected type `String' with actual type `Int'
    Expected type: (State Int Int, State String String)
      Actual type: (StateT Int Identity Int,
                    StateT String Identity Int)

Because I presume the two get must return the same type, which is an unfortunate restriction. Any thoughts on how to get around this?

2

2 Answers

4
votes

The solution is to use a state monad with a product of your states:

m :: State (Int, String) ()

Then you can run an operation that interacts with one of the two fields of the product using zoom and _1/_2 from the lens library, like this:

m = do
    n <- zoom _1 get
    zoom _2 $ put (show n)

To learn more about this technique, you can read my blog post on lenses which goes into much more detail.

4
votes

It can't be done the way you want. Suppose there would be a way how to get the current state out of the left monad. Then you'd have a function of type

getLeft :: Product (State a) (State b) a

which is isomorphic to (State a a, State b a).

Now we can choose to throw away the left part and run only the right part:

evalState (snd (runProduct getLeft)) () :: a

So we get an inhabitant of an arbitrary type a.

In other words, the two monads inside Product are completely independent. They don't influence each other and can be run separately. Therefore we can't take a value out of one and use it in another (or both of them).