Haskell GUI编程和延迟评估的问题

时间:2018-03-31 19:00:52

标签: user-interface haskell lazy-evaluation

为了尝试将Haskell用于图形应用程序,我在使用GUI进行评估时遇到了很多麻烦。

例如,我有时会尝试在程序中的某个位置创建小部件,打包并将其发送到程序的其他组件。

这样做效果不好,通常永远不会导致窗口小部件显示,因为懒惰的评估会在窗口小部件被放到屏幕上之前丢弃窗口小部件。

所以我想知道,除了投入一堆seq和其他变体以鼓励急切评估之外,可以/如何处理这个问题?

我已经找到了答案,但无法找到任何与此问题相关的内容。

编辑:示例代码

下面的代码产生了一个空窗口。

{-# LANGUAGE RecursiveDo #-}
    -- allows recursive do notation
    -- mdo
    --  ...

import Control.Monad
import Control.Monad.IO.Class
import qualified Data.Map.Strict as Map
import qualified Data.List       as List
import System.Random

import Graphics.UI.WX hiding (Event)
import Graphics.UI.WXCore as WXCore
import Reactive.Banana
import Reactive.Banana.WX

boardWidth, boardHeight :: Int
boardWidth  = 41
boardHeight = 81

main :: IO ()
main = start tetris

tetris = do
    ff <- frame [text      := "Tetris"
                ,bgcolor   := white
                ,resizeable:= False]

    p <- panel ff []
    set ff [ layout := minsize (sz 100 100) $ widget p]

    pps <- return $ Map.fromList $ map (\l@(x,y) -> (l, button p []))
                    [(x,y) | x <- [1..(boardWidth  `div` 2)], y <- [1..(boardHeight `div` 2)]]

    -- p <- pps Map.! (1,2)

    d <- return $ map (\(x,y_m) -> y_m >>= (\y -> set ff [ color := white, layout := minsize (sz 300 300) $ widget y ])) $ Map.toList pps

    -- let networkDescription :: Moment IO ()
    return ff

线p <- pps Map.! (1,2)导致图块显示为1,2,没有正确的尺寸。

1 个答案:

答案 0 :(得分:1)

  

这并不能很好地解决这个问题,通常永远不会导致窗口小部件显示,因为懒惰的评估在它有机会被放到屏幕上之前丢弃了窗口小部件。

正如Thomas M. DuBuisson所说,这是一种误诊:这里的问题与懒惰的评估无关(在任何情况下,懒惰的评估都不会丢弃你实际尝试使用的值) 。让我们来看看您应该将按钮添加到面板的行:

d <- return $ map (\(x,y_m) ->
    y_m >>= (\y -> set ff [ color := white, layout := minsize (sz 300 300) $ widget y ]))
    $ Map.toList pps

Map.toList pps的类型为[((Int, Int), IO (Button ())]。您对map的使用会将其更改为[IO ()](参见the type of set), IO次操作列表。实际上,您正在设置添加按钮的操作,但实际上从未实际运行它们。为此,您需要traverse_而不是Data.Foldable的{​​{1}}:

map

(有关traverse_ (\(x,y_m) -> y_m >>= (\y -> set ff [ color := white, layout := minsize (sz 300 300) $ widget y ])) $ Map.toList pps 及其堂兄traverse_的更多信息,请参阅this answer,以及其他父母问题的答案。)

请注意,除非我严重误读特定于WX的代码,否则仍然无法完成您想要的。当您为列表元素生成的每个traverse操作重置框架的布局时,您最终只会使用框架中的最后一个按钮。为了获得合理的布局,您必须实际使用坐标值,以及WX布局组合器以使用所有按钮设置单个布局。参看the Graphics.UI.WX.Layout documentation

最后一点,在do-block中这样的代码......

IO

...总是多余的:它可以替换为:

bar <- return foo

在你的情况下,这样做会更明显地表明你并没有真正运行这些行动。

(您对let bar = foo Map.fromList的使用也是多余的,但我猜您已经怀疑过了。)