Combining monads in Haskell

haskell, io, state-monad

Solution

What you want is `StateT s IO (String, Bool)`, where `StateT` is provided by both `Control.Monad.State` (from the `mtl` package) and `Control.Monad.Trans.State` (from the `transformers` package).

This general phenomenon is called a monad transformer, and you can read a great introduction to them in Monad Transformers, Step by Step.

There are two approaches to defining them. One of them is found in the `transformers` package which uses the `MonadTrans` class to implement them. The second approach is found in the `mtl` class and uses a separate type-class for each monad.

The advantage of the `transformers` approach is the use of a single type-class to implement everything (found here):

class MonadTrans t where
    lift :: Monad m => m a -> t m a

`lift` has two nice properties which any instance of `MonadTrans` must satisfy:

(lift .) return = return
(lift .) f >=> (lift .) g = (lift .) (f >=> g)

These are the functor laws in disguise, where `(lift .) = fmap`, `return = id` and `(>=>) = (.)`.

The `mtl` type-class approach has its benefits, too, and some things can only be cleanly solved using the `mtl` type-classes, however the disadvantage is then that each `mtl` type-class has its own set of laws you have to remember when implement instances for it. For example, the `MonadError` type-class (found here)is defined as:

class Monad m => MonadError e m | m -> e where
    throwError :: e -> m a
    catchError :: m a -> (e -> m a) -> m a

This class comes with laws, too:

m `catchError` throwError = m
(throwError e) `catchError` f = f e
(m `catchError` f) `catchError` g = m `catchError` (\e -> f e `catchError` g)

These are just the monad laws in disguise, where `throwError = return` and `catchError = (>>=)` (and the monad laws are the category laws in disguise, where `return = id` and `(>=>) = (.)`).

For your specific problem, the way you would write your program would be the same:

do
  -- get the number of games from the command line (already written)
  results <- mapM (\game -> playGame game getStdGen) [1..numberOfGames]

... but when you write your `playGame` function it would look either like:

-- transformers approach :: (Num s) => StateT s IO ()
do x <- get
   y <- lift $ someIOAction
   put $ x + y

-- mtl approach :: (Num s, MonadState s m, MonadIO m) => m ()
do x <- get
   y <- liftIO $ someIOAction
   put $ x + y

There are more differences between the approaches that become more apparent when you start stacking more than one monad transformer, but I think that's a good start for now.

Problem

I am trying to write a Spider Solitaire player as a Haskell learning exercise. My `main` function will call a `playGame` function once for each game (using `mapM`), passing in the game number and a random generator (`StdGen`). The `playGame` function should return a `Control.Monad.State` monad and an IO monad that contains a `String` showing the game tableau and a `Bool` indicating if the game was won or lost. How do I combine the `State` monad with the `IO` monad for the return value? What should the type declaration for `playGame be? ``` playGame :: Int -> StdGen a -> State IO (String, Bool) ``` Is the `State IO (String, Bool)` correct? If not, what should it be? In `main`, I plan on using ``` do -- get the number of games from the command line (already written) results <- mapM (\game -> playGame game getStdGen) [1..numberOfGames] ``` Is this the correct way to call `playGame`?

Original source