gpt4 book ai didi

haskell - 如何减少 Haskell 中必须传递的参数数量?

转载 作者:行者123 更新时间:2023-12-02 13:09:09 25 4
gpt4 key购买 nike

我正在非常缓慢地跟上 Haskell 的速度,试图获得一个可用的 gui 工具,等等。我遵循了使用 Glade 创建一个简单的 GUI 应用程序的基本教程,现在我正在尝试将其模块化。特别是,我想利用函数而不是在 main 中完成所有操作。我做的第一件事是创建单独的函数来访问按钮并关联单击按钮时要执行的代码。它工作得很好,但如果你看看下面的代码,我必须随身携带整个林间空地 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

最佳答案

这确实是augustss评论中的建议。完全未经测试,但这将帮助您入门:

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 转换器教程。

<小时/>

让我再试一次。我正在做的是将两个想法结合起来,您可以分别解决这些想法,然后将其重新组合在一起:

  1. Reader 单子(monad)
  2. Monad 变压器

您可以首先阅读这些内容来尝试理解 Reader monad:

基本上,Reader 是一个 monad,它构造依赖于缺失的隐式“环境”值的值。在 Reader monad 中,有一个名为 ask::Reader r r 的操作,其结果是环境值。

因此,我们的想法是,只要有 GladeXML -> some,您就可以将该函数重写为 Reader GladeXML some 类型的单子(monad)操作。例如,我上面的示例的简化(没有 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

但是,由于您在这里同时使用了 ReaderIO,因此您想要做的是创建一个具有两者“功能”的组合 monad。这就是 monad 转换器添加到图片中的内容。从概念上讲,ReaderT GladeXML IO a 是一个可以访问“隐式”GladeXML 值的 IO 操作:

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 ...

关于haskell - 如何减少 Haskell 中必须传递的参数数量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24396299/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com