[Haskell-cafe] adding state in GUIs (qtHaskell)

Jeremy O'Donoghue jeremy.odonoghue at gmail.com
Fri Sep 11 11:55:50 EDT 2009


Michael Mossey wrote:
> Duncan Coutts wrote:
>> On Wed, 2009-09-09 at 18:29 -0700, Michael P Mossey wrote:
>>> I'm trying to learn qtHaskell. I realize few people on this list
>>> know anything about qtHaskell, but I have a question that probably
>>> relates to all GUIs as implemented in Haskell. I just need a hint
>>> that could help me figure out the next step, which I might be able
>>> to infer from the qtHaskell API.
>>
>> Ultimately it's done by some kind of mutable state, either an IORef,
>> MVar or a thread.
In wxHaskell, the 'simplest' way to code this looks something like the
following (literate Haskell)

Structure containing 'state' of all of the GUI objects

> data UIState = UIState { uiConnect    :: Button ()
>                        , uiPort       :: TextCtrl ()
>                        , uiUser       :: TextCtrl ()
>                        , uiPasswd     :: TextCtrl ()
>                        , uiSandbox    :: TextCtrl ()
>                        , uiClients    :: ComboBox ()
>                        , uiChanges    :: SingleListBox ()
>                        , uiChangeInfo :: TextCtrl ()
>                        , uiOrigin     :: TextCtrl ()
>                        , uiUpdate     :: TextCtrl ()
>                        , uiFrame      :: Frame ()
>                        }

> uiState = unsafePerformIO $ newMVar (Nothing :: Maybe UIState)

Ensure that we initialize exactly once...

> uiInitState bt pt us pw sb cl ci ch or up f =
>     takeMVar uiState >>= \st ->
>     case st of
>       Nothing -> let st' = UIState bt pt us pw sb cl ci ch or up f in
>                  putMVar uiState (Just st')
>       Just _  -> return ()

Get the mutable state.
Note that in the error case we deliberately do not put the MVar back, as
a means
to block all threads waiting on the MVar (as this would indicate a general
programming/threading issue to be identified).

> getMVarState mv txt =
>     takeMVar mv >>= \may_st ->
>     case may_st of
>       Nothing -> error (txt ++ " is not available")
>       Just st -> putMVar mv may_st >>
>                  return st

Fetch the UI state - this will fail fatally if we fetch before state is
initialized

> uiGetState = getMVarState uiState "UI state"

I don't have anything as neat to show you as Duncan's suggetion (I'd
also be interested to see a cleaner way to do it - this sort of code
always grates a little with me, although all of the major Haskell GUI
bindings seem to need a similar programming style.

However, at the most basic 'trying it out' level, I suspect that
something very like this will work just as well for qtHaskell as it does
for wxHaskell.
>> On top of these you can layer nicer stuff like a state monad (with a
>> 'runState' function that saves and restores from an IORef).
>>
>> A personal favourite of mine is having the GUI event handler post data
>> over a channel to a thread. That thread reads from the channel and deals
>> with the events. The state of the GUI app is then held as local
>> parameters in that thread.
>>
>> Doing this of course requires that the GUI lib you're using can cope
>> with normal Haskell (forkIO) threads. This is possible with gtk2hs, I
>> don't know about the others.
Regards
Jeremy
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/haskell-cafe/attachments/20090911/2c02ff93/attachment.html


More information about the Haskell-Cafe mailing list