[Haskell-cafe] Stacking monads - beginner design question
Adam Smyczek
adam.smyczek at gmail.com
Wed Jan 30 02:22:57 EST 2008
It works like a charm,
thanks a lot Jonathan!
Adam
On Jan 29, 2008, at 10:26 PM, Jonathan Cast wrote:
> 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