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`?