【问题标题】:Trouble with Haskell GUI programming and lazy evaluationHaskell GUI 编程和惰性求值的问题
【发布时间】:2023-03-12 23:00:01
【问题描述】:

在尝试将 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 &lt;- pps Map.! (1,2) 行导致 1、2 处的磁贴显示不正确。

【问题讨论】:

  • 这听起来像是误诊但没有任何代码我们无能为力。我建议你制作一个 MCVE。
  • 你说,“除了放一堆seq,如何处理这个问题?”。您是否验证过放入一堆 seq 确实可以解决这个问题?
  • 我现在无法写出完整的答案;无论如何,这里的主要问题是您实际上并没有运行将按钮添加到框架的IO 操作。您可能希望在列表中使用traverse,而不是mapreturn

标签: user-interface haskell lazy-evaluation


【解决方案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 而不是 map

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

(有关traverse_ 及其表亲traverse 的更多信息,请参阅this answer,以及对父问题的其他答案。)

请注意,除非我严重误读了特定于 WX 的代码,这仍然无法满足您的要求。由于您为列表元素生成的每个IO 操作都会重置框架的布局,因此您最终只会得到框架中的最后一个按钮。要获得合理的布局,您必须实际使用坐标值以及 WX 布局组合器来设置包含所有按钮的单一布局。参照。 the Graphics.UI.WX.Layout documentation.

最后,do-block 中的代码是这样的......

bar <- return foo

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

let bar = foo

在您的情况下,这样做会更明显地表明您实际上并没有运行这些操作。

(你对Map.fromListMap.toList的使用也是多余的,但我猜你已经怀疑了。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-13
    • 1970-01-01
    相关资源
    最近更新 更多