[Haskell-cafe] Current situation regarding global IORefs

Robert Dockins robdockins at fastmail.fm
Fri Apr 21 14:22:45 EDT 2006


On Apr 21, 2006, at 1:27 PM, Brian Hulley wrote:

> Robert Dockins wrote:
>> On Apr 21, 2006, at 10:34 AM, Brian Hulley wrote:
>>> Robert Dockins wrote:
>>>> On Apr 21, 2006, at 9:56 AM, Brian Hulley wrote:
>>>>
>>>>> Hi -
>>>>> I've run into the global mutable state problem described in  
>>>>> http://
>>> [snip]
>>>
>>> There is only one GUI for the application and only one control in
>>> it can have the keyboard focus so it seems natural to use global
>>> state here
>>
>> I'd suggest you consider not making those assumptions... they are the
>> kinds of assumptions that can make later code reuse and maintenance
>> more difficult than it should be.  (Obviously, if code reuse/
>> maintenance is a low priority then it doesn't matter).
>>
>>> , but I suppose I could also look into using a state monad. The
>>> advantage (perhaps also disadvantage ;-) ) of global state is that
>>> it allows me to easily convert all my old C++ singleton classes to
>>> Haskell modules...
>>
>> <ramble type="somewhat coherent">
>> Ahhh... the singleton pattern.  There is a debate among OO theorists
>> about whether the singleton pattern is actually a good idea.  I tend
>> to side with those who say that it is Just Wrong. [snip]
>
> Thanks for the comments. I've now changed everything so that  
> controls use a ManagerM monad which wraps up the state instead of  
> using the IO monad so there are no longer any global variables. It  
> wasn't as difficult as I had thought and as you say it makes  
> everything much more scalable, although at the expense of having to  
> use liftIO in various places.

This is true, and mildly irritating.  One additional (very  
unfortunate) point is that higher-order IO monad combinators will not  
work on your monad, eg, the ones in Control.Exception.  I hope H'  
will generalize the types to (use MonadIO)  these combinators to make  
this sort of thing easier, because I think this is a great way to  
structure programs.  *makes mental note to create a ticket for this*   
Sometimes I also think it would be nice if all the standard lib  
functions with IO types would instead take arbitrary MonadIO types,  
so you could avoid having to write down liftIO all the time....

> I've defined my state monad by:
>
> data MState = MState {keyboard:: !Maybe Control} -- etc - other  
> state here also
> type ManagerM a = StateT MState IO a
>
> and everything works ok. However if I try to use a newtype instead  
> of a type (to completely hide the representation) eg
>
> newtype ManagerM a = ManagerM (StateT MState IO a) deriving (Monad,  
> MonadIO, MonadState)
>
> it won't compile.

Are you compiling with -fglasgow-exts?  You're relying on generalized  
newtype deriving, which is a GHC extension.

http://www.haskell.org/ghc/docs/latest/html/users_guide/type- 
extensions.html#newtype-deriving

If that's not it, what's the error you are getting?

> Does this mean it is not possible to wrap combined monads in a  
> newtype? I notice that the examples in tutorials I've looked at  
> tend to always just use type instead of newtype.

I usually use a newtype myself; but then I usually roll my own monads  
instead of using monad transformers (not a value judgement, just habit).

> Another point is that I'm not sure what is the "proper" way to  
> represent the state itself ie should each component of the state be  
> a separate IORef to avoid having to copy the whole state each time  
> or is it better practice to just use an immutable record as I've  
> done above?

I usually use immutable records as you have done; it somehow "feels  
better".  Unfortunately, going this way exposes you to the clunkiness  
of Haskell's record system.  If all your record components are  
declared with a bang, you may be able to coerce the compiler to unbox  
the record (-funbox-strict-fields, I think), which would prevent  
copying altogether.  Immutable records are also a little nicer to the  
garbage collector.  However, I've never actually tried to measure the  
performance difference.

If you're going to use a record of IORefs, you should probably go  
with ReaderT instead.

> Thanks, Brian.


Rob Dockins

Speak softly and drive a Sherman tank.
Laugh hard; it's a long way to the bank.
           -- TMBG





More information about the Haskell-Cafe mailing list