Combining IO and state monads

gilesb@cpsc.ucalgary.ca gilesb@cpsc.ucalgary.ca
Thu, 15 May 2003 11:33:21 -0600 (MDT)


On 15 May, Tomasz Zielonka wrote:
> On Thu, May 15, 2003 at 12:14:31PM +0100, Graham Klyne wrote:
>> [..]
>> 
>> The workspace would seem to be appropriately manipulated using a form of 
>> state monad.  And the I/O operations would be performed through an IO 
>> monad.  What I'm unsure about is the best way to combine these so that the 
>> real-world state (IO) and workspace state are updated (threaded?) in 
>> parallel.
>>
>> [...]
>>

> 
> You probably want Monad Transformers. Both GHC and Hugs provide a
> library of monad transformers for adding state, continuations,
> exceptions, etc. They can be smoothly use with IO monad. Here is an
> example of using state monad transformer.
>  [...]

I find this works well too. I typically put these in another module and
create type synonyms for them (and sometimes state getters/setters).
This allows one to easily change the type in the state without rewriting
all the functions that use your type. Similarly, it becomes easy to
change the Monad as well.  For instance, you may start with State
combined with IO and then find you need to also combine  exception in
later.

As an example of how I set up the types, the following is a short
excerpt from code for a stack machine Assembler that I wrote. (AssState
etc are other types declared elsewhere.  The Monad transformer types
typically follow the pattern of ending in "T" and taking a Monad as one
of their subordinate types.

> import MonadState
> import MonadError

> type Assem = ErrorT  [Char] (StateT AssState IO )
> type AssemOp = ErrorT  [Char] (StateT String IO )

> type IOMachine = ErrorT [Char] (StateT (Machine,ProgramState) IO )


and some of the getters/setters are:

> getMach::IOMachine Machine
> getMach = do (m,_ ) <-get
> 	     return m

> putMach::Machine->IOMachine()
> putMach m = do (_,ps)<- get
	       put (m,ps)

> putStrIOM::String->IOMachine()
> putStrIOM = lift.lift.putStr
> putStrLnIOM::String->IOMachine()
> putStrLnIOM = lift.lift.putStrLn

> getStack::IOMachine (MStack StackPtr StackVal)
> getStack = do mach <-getMach
> 	      return $ stack mach
-- 
Brett G. Giles
Grad Student, University of Calgary
Formal Methods, Category Theory, Semantics of Programming
http://www.cpsc.ucalgary.ca/~gilesb     mailto:gilesb@cpsc.ucalgary.ca