[Haskell-cafe] Stacking monads - beginner design question

Jonathan Cast jonathanccast at fastmail.fm
Wed Jan 30 01:26:04 EST 2008


On 29 Jan 2008, at 9:44 PM, Adam Smyczek wrote:

> Hi,
>
> My application has to manage a data set. I assume the state monad  
> is designed for this.
> The state changes in functions that:
> a. perform IO actions and
> b. return execution status and execution trace (right now I'm using  
> WriteT for this).
>
> Is the best solution:
> 1. to build a monad stack (for example State -> Writer -> IO) or
> 2. to use IORef for the data set or
> 3. something else?
>
> Are monad stacks with 3 and more monads common?

I'd say they're fairly common, yes; at least, they don't jump out at  
me as bad style (especially when the monads are fairly orthogonal, as  
here).

> How could an example implementation look like?

newtype Program alpha
   = Program { runProgram :: StateT Config (WriterT [String] IO) alpha }
   deriving (Functor, Monad, MonadWriter, MonadState)

>
> What I have for now is:
>
> -- Status
> data Status = OK | FAILED deriving (Show, Read, Enum)
>
> -- Example data set manages by state
> type Config = [String]
>
> -- WriterT transformer
> type OutputWriter = WriterT [String] IO Status
>
> -- example execute function
> execute :: [String] -> OutputWriter

execute :: [String] -> Program Status

> execute fs = do
> 	rs <- liftIO loadData fs
> 	tell $ map show rs
> 	return OK
>
> -- run it inside e.g. main
> (s, os) <- runWriterT $ execute files

(s', (s, os)) <- runWriterT (runStateT (runProgram $ execute files)  
inputstate)

It's a bit tricky, since you have to write it inside-out, but it  
should only type check if you've got it right :)

> How do I bring a state into this, for example for:
> execute fs = do
> 	?? conf <- get ?? -- get Config from state

Right.

> 	rs <- liftIO loadData conf fs
> 	?? set conf ?? -- change Config and set to state

Right.

> 	tell "new state:"

Right.

> 	tell $ show conf
> 	return OK
>
> Do I have to use

Depends on what you mean by `have to'.  If you don't want to thread  
the state yourself, and you don't want to use an IORef, you'll need  
some implementation of a state monad.  That will have to be in the  
form of a monad transformer applied to IO, so the easy answer is `yes'.

> and how do I use StateT in this context:
> data DataState = StateT Config OutputWriter ??

This is parenthesized wrong; the output type goes outside the  
parentheses around WriterT:

StateT Config (WriterT [String] IO) Status

not

StateT Config (WriterT [String] IO Status)

> and how do I run it runStateT . runWriterT?

Other way 'round, as above.

HTH

jcc



More information about the Haskell-Cafe mailing list