我在Haskell中慢慢加快速度,尝试使用gui toolgit等等。我按照使用glade的基本教程创建了一个简单的GUI应用程序,现在我试图尝试模块化它。特别是,我想利用函数而不是在main中执行所有操作。我做的第一件事是创建单独的函数来访问按钮和关联单击按钮时要执行的代码。它工作正常,但如果你看下面的代码,我必须携带整个glade XML"变量"和我在一起。我意识到我们不会在Haskell中做全局,但在我看来,必须有一个更好的机制,而不是在函数中携带每个变量。显然在OO世界中,XML的东西只是一个类中的实例变量,因此在任何地方都可以隐式使用。什么是"对"在Haskell世界中这样做的方法?
module Main (main) where
import Graphics.UI.Gtk
import Graphics.UI.Gtk.Glade
getButton :: GladeXML -> String -> IO Button
getButton gladeXML buttonName =
xmlGetWidget gladeXML castToButton buttonName
onButtonClick :: GladeXML -> String -> [IO a] -> IO ()
onButtonClick gladeXML buttonName codeSequence = do
aButton <- getButton gladeXML buttonName
_ <- onClicked aButton $ do -- Run the sequence of operations when user clicks
sequence_ codeSequence
return ()
loadGladeFile :: FilePath -> IO (Maybe GladeXML)
loadGladeFile filename = do
g <- xmlNew filename
return g
main :: IO ()
main = do
_ <- initGUI -- Setup
-- Load the Glade XML file
Just xml <- loadGladeFile "tutorial.glade"
-- Create main window (everything inside will be created too)
window <- xmlGetWidget xml castToWindow "window1"
-- Define what to do when we quit
_ <- onDestroy window mainQuit
-- Show the wondow
widgetShowAll window
-- Associate an onClick event with a button
onButtonClick xml "button1" [putStrLn "Hello, world"]
-- Off we go
mainGUI
答案 0 :(得分:11)
这真是奥古斯都的建议。评论。彻底未经测试,但这会让你开始:
import Control.Applicative
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Reader
import Graphics.UI.Gtk
import Graphics.UI.Gtk.Glade
getButton :: String -> ReaderT GladeXML IO Button
getButton buttonName =
do gladeXML <- ask
return . lift $ xmlGetWidget gladeXML castToButton buttonName
运行ReaderT GladeXML IO
操作:
-- Well, you should probably just use `runReaderT` directly, but at least the
-- type signature here is instructive.
runGladeXMLReader :: ReaderT GladeXML IO a -> GladeXML -> IO a
runGladeXMLReader = runReaderT
尝试阅读Control.Monad.Trans.Reader
上的文档和一些monad变换器教程。
让我再试一次。我正在做的是将两个你可以单独解决的想法结合起来,然后重新组合起来:
Reader
monad 您可以从阅读这些内容开始,尝试了解Reader
monad:
Reader
(&#34;未来价值观世界&#34;)基本上,Reader
是一个monad,它构造依赖于缺失的,隐含的&#34;环境的值。值。在Reader
monad中,有一个名为ask :: Reader r r
的操作,其结果是环境值。
所以我的想法是,无论你有GladeXML -> something
,你都可以将该函数重写为Reader GladeXML something
类型的monadic动作。例如,简化上面的例子(没有monad变换器):
getButton :: String -> Reader GladeXML (IO Button)
getButton buttonName = do
-- The variable gladeXML gets the value of the "implicit" GladeXML value
gladeXML <- ask
-- Now we use that value as an argument to the xmlGetWidget function.
return $ xmlGetWidget gladeXML castToButton buttonName
您使用Reader
的方式是使用runReader :: Reader r a -> r -> a
功能。示意性地:
{- NOTE: none of this is guaranteed to even compile... -}
example :: IO Button
example = do
_ <- initGUI -- Setup
Just xml <- loadGladeFile "tutorial.glade"
runReader (getButton "button1") xml
但是,由于您在此处同时使用Reader
和IO
,因此您要做的就是制作一个具有&#34; power&#34;的组合monad。两者。这是monad变形金刚添加到图片中的内容。概念上,ReaderT GladeXML IO a
是IO
操作,可以访问&#34;隐含&#34; GladeXML值:
getButton :: String -> ReaderT GladeXML IO Button
getButton buttonName =
do gladeXML <- ask
-- There is one catch: to use any IO action, you have to prefix it with
-- the `lift` function...
button <- lift $ xmlGetWidget gladeXML castToButton buttonName
return button
-- I've refactored this slightly to *not* take a list of actions.
onButtonClick :: String -> ReaderT GladeXML IO a -> ReaderT GladeXML IO ()
onButtonClick gladeXML buttonName action = do
aButton <- getButton buttonName
xml <- ask
_ <- lift $ onClicked aButton (runReaderT action xml)
return ()
-- This is the piece of code that illustrates the payoff of the refactoring.
-- Note how there is no variable being passed around for the xml. This is
-- because I'm making a "big" ReaderT action out of small ones, and they will
-- all implicitly get the same `GladeXML` value threaded through them.
makeButton1 :: ReaderT GladeXML IO Button
makeButton1 =
do button1 <- getButton "button1"
onButtonClick "button1" $ do
lift $ putStrLn "Hello, world"
return button1
-- The `main` action just fetches the `GladeXML` value and hands it off to the
-- actual main logic, which is a `ReaderT` that expects that `GladeXML`
main :: IO ()
main = do
xml <- ...
runReaderT actualMain xml
actualMain :: ReaderT GladeXML IO ()
actualMain = do ...