How to deal with application state in Gtk2Hs
Asked Answered
H

1

11

Trying to learn to write applications with Gtk2Hs I'm getting difficulties bridging the gap between the event driven Gtk2HS and the persistent state of my model. So to simplify, lets say that I have this simple application

module Main where

import Graphics.UI.Gtk
import Control.Monad.State

main = do
    initGUI
    window <- windowNew
    button <- buttonNew
    set button [buttonLabel := "Press me"]
    containerAdd window button

    -- Events
    onDestroy window mainQuit
    onClicked button (putStrLn ---PUT MEANINGFUL CODE HERE---)

    widgetShowAll window
    mainGUI

and the state of my application is how many times the button has been pressed. Seeing other posts like this they rely on MVars or IORefs which do not seem satisfactory to me, because in the future maybe I will want to refactor the code so the state lives on its own context.

I think that the solution should use the State monad using a step function like:

State $ \s -> ((),s+1)

but I'm not sure about the implications, how to do that in the above code or even if that monad is the right solution to my problem.

Holinshed answered 17/8, 2012 at 9:1 Comment(0)
P
7

There's basically two approaches:

  1. Use a pointer of some kind. This is your IORef or MVar approach. You can hide this behind a MonadState-like interface if you like:

    newtype GtkT s m a = GtkT { unGtkT :: ReaderT (IORef s) m a } deriving (Functor, Applicative, Monad, MonadIO)
    runGtkT = runReaderT . unGtkT
    
    instance MonadIO m => MonadState s (GtkT s m) where
        get   = GtkT (ask >>= liftIO . readIORef)
        put s = GtkT (ask >>= liftIO . flip writeIORef s)
    
  2. Pull an "inversion of control" style trick. Write a callback that prints a number, then replaces itself with a new callback that prints a higher number.

If you try to use State or StateT directly, you're gonna have a bad time.

Pinetum answered 17/8, 2012 at 9:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.