【问题标题】:Monad: [UI Element] vs [Element]Monad:[UI 元素] vs [元素]
【发布时间】:2016-08-15 12:22:16
【问题描述】:

在下面的代码示例中,我尝试创建一个包含许多选择元素的框,并使用行为将它们的选择组合到一个值列表中。 (代码在 ghci 中编译/运行,只需要threepenny-gui)

{-# LANGUAGE RecursiveDo #-}
module Threepenny.Gui where

import Prelude hiding (lookup)
import Control.Monad
import Data.List
import Data.Traversable
import Data.Maybe
import Data.Monoid
import qualified Data.Map as Map
import qualified Data.Set as Set

import qualified Graphics.UI.Threepenny as UI
import Graphics.UI.Threepenny.Core hiding (delete)

{-----------------------------------------------------------------------------
    (#) Reverse function application: flip $
    (#+) Append DOM elements as children to given element: parent #+ children
    (#.) Returns UI Element with CSS class changed to second parameter
------------------------------------------------------------------------------}
gui :: IO ()
gui = startGUI defaultConfig setup

fixedTextarea = UI.textarea # set style [("resize", "none"), ("height", "14px"), ("width", "500px")]

combinedBeh :: MonadIO m => [Element] -> m (Behavior ([Maybe Int]))
combinedBeh sl = sequenceA <$> sequence blist
  where blist  = fmap (stepper Nothing . UI.selectionChange ) sl

selectDivWrong :: UI (Element, Behavior [Maybe Int])
selectDivWrong = do 
  let select options = UI.select 
        # set style [("display","inline-block"), ("width", "150px"), ("margin", "0px 0px 4px 0px")]
        #+ fmap (\x -> UI.option # set UI.text (show x)) options
      selectionList :: [UI Element]
      selectionList = replicate 6 $ select [0, 1, 2, 3, 4, 5]

  selectionList' <- (sequence selectionList :: UI [Element])
  bSelectionList <- combinedBeh selectionList'
  mainBox        <- UI.mkElement "selectDiv"
    # set style [("display","inline-block"), ("background-color", "#333344"),
     ("height", "200px"), ("width", "150px"), ("padding", "1px")] --
    #+ (selectionList) -- unsequenced list of UI elements. The behavior (bSelectionList) should have all the info it needs though(?).
-- why does (#+) not have the same UI info as bSelectionList ?

  return (mainBox, bSelectionList)

selectDivCorrect :: UI (Element, Behavior [Maybe Int])
selectDivCorrect = do 
  let select options = UI.select 
        # set style [("display","inline-block"), ("width", "150px"), ("margin", "0px 0px 4px 0px")]
        #+ fmap (\x -> UI.option # set UI.text (show x)) options
      selectionList :: [UI Element]
      selectionList = replicate 6 $ select [0, 1, 2, 3, 4, 5]

  selectionList' <- (sequence selectionList :: UI [Element])
  bSelectionList <- combinedBeh selectionList'
  mainBox        <- UI.mkElement "selectDiv"
    # set style [("display","inline-block"), ("background-color", "#333344"),
     ("height", "200px"), ("width", "150px"), ("padding", "1px")] --
    #+ (fmap pure selectionList')

  return (mainBox, bSelectionList)

setup :: Window -> UI ()
setup window = void $ mdo
  (sDiv1, bSDiv) <- selectDivWrong
  text1   <- fixedTextarea # sink UI.text (show <$> bSDiv) 

  (sDiv2, bSDiv2) <- selectDivCorrect
  text2   <- fixedTextarea # sink UI.text (show <$> bSDiv2) 


  getBody window 
    #+ [grid
        [ [element sDiv1]
        , [element text1]
        , [element sDiv2]
        , [element text2]
        ]]  
    # set style [("background-color", "#eeeeee")]

最初我想使用selectDivWrong,但发现我需要将其修改为selectDivCorrect。我的问题是我不明白为什么存在功能差异。在这两种情况下,selectionList 包含所有需要添加的元素,bSelectionList 结合了所有行为。我不确定UI 如何处理所有状态和事件(而且我还没有大量使用 monads/applicatives),但我怀疑在正确的版本中,组合的UI 上下文被添加到' top level' 并因此传递给(#+)(或UI.mkElement?),但在错误版本的UI Elements 列表中仍未使用。

不过,我仍然不确定我是否遗漏了什么。我真的很想确定并找到有助于在未来识别此类问题的解释,因为我基本上通过反复试验找到了解决方案。 (也可以随意重命名问题...)

【问题讨论】:

    标签: haskell threepenny-gui


    【解决方案1】:

    他们的关键点是UI Element 类型表示创建/操作/返回事物的(单子)动作,而Element 类型表示事物本身。有时,前者创造了一个新事物,有时它返回了一个旧事物。

    在您的情况下,selectionList :: [UI Element]创建事物(每个)的操作列表。

    在错误的版本中,您首先使用sequence 按顺序执行所有操作,但随后您还将列表传递给#+ 组合器,该组合器在内部也按顺序执行所有操作。因此,动作被执行了两次,因此每个元素被创建了两次。

    在正确的版本中,你使用sequence依次执行所有动作,并在selectionList'列表中保留对应的新事物(Element)。所有元素都已创建一次,然后您只需将它们传递给接下来的任何内容。组合器pure 构建了一个简单地返回现有事物的操作,这就是它的工作原理。


    最后,#+ 的类型可能有点混乱,如果它接受列表 [Element] 而不是列表 [UI Element] 会更透明。不过,后者减少了句法噪音,因为它减少了命名单个元素的需要。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-16
      • 2018-11-06
      • 2011-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多