[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