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.
There's basically two approaches:
Use a pointer of some kind. This is your
IORef
orMVar
approach. You can hide this behind aMonadState
-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)
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.
来源:https://stackoverflow.com/questions/12002814/how-to-deal-with-application-state-in-gtk2hs