11

尝试学习使用 Gtk2Hs 编写应用程序时,我在弥合事件驱动的 Gtk2HS 和我的模型的持久状态之间的差距时遇到了困难。所以为了简化,假设我有这个简单的应用程序

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

我的应用程序的状态是按钮被按下了多少次。看到其他类似这样的帖子,他们依赖于 MVar 或 IORef,这对我来说似乎并不令人满意,因为将来我可能会想要重构代码,以便状态存在于自己的上下文中。

我认为解决方案应该使用 State monad 使用 step 函数,例如:

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

但我不确定其中的含义,如何在上面的代码中做到这一点,或者即使那个 monad 是我问题的正确解决方案。

4

1 回答 1

7

基本上有两种方法:

  1. 使用某种指针。这是您的IORef方法MVar。如果您愿意,可以将其隐藏在类似MonadState- 的界面后面:

    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. 拉一个“控制反转”风格的把戏。编写一个打印数字的回调,然后用打印更高数字的新回调替换自身。

如果您尝试使用StateStateT直接使用,您将度过一段糟糕的时光。

于 2012-08-17T09:45:24.280 回答