2016-04-22 18 views
1

次のコードサンプルでは、​​いくつかのselect要素を含むボックスを作成し、その選択を動作を使用して値リストに結合しようとしました。 Monad:[UI要素] vs [要素]

{-# 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にそれを修正する必要があることを考え出した(コードのコンパイル/ちょうど三文-GUIでGHCiの中で実行されます)。私の問題は、なぜ機能的な違いがあるのか​​分かりません。どちらの場合も selectionListには追加が必要な要素がすべて含まれ、 bSelectionListはすべての動作を組み合わせています。私はどのように UIがすべての状態とイベント(と私はまだ多くのモナド/アプリケーションを使用していない)を処理するかわからないが、正しいバージョンでは、組み合わせの UIコンテキストが 'トップレベル'に追加され、したがって (#+)(または UI.mkElement?)に渡されますが、間違ったバージョンの UI Elementのリストでは未使用のままです。

私はまだ何かが欠けているかどうかはまだ分かりません。私は実際に試行錯誤を経て解決策を見つけたので、この種の問題を特定するのに役立つ説明を見つけることが本当に好きです。

答えて

2

重要な点は、タイプUI Elementは物を作成/操作/返すアクション(モノラル)を表し、タイプElementは物そのものを表します。時には、前者は新しいものを作り、時には古いものを返します。

あなたの場合、selectionList :: [UI Element]は、というものを作成するアクションのリストです。(それぞれ)。

間違ったバージョンでは、最初にsequenceを使用してすべてのアクションを順番に実行しますが、コンビネータ#+にリストを渡します。このコンビネータは内部的にすべてのアクションも順番に実行します。したがって、アクションは2回実行されるため、各要素は2回作成されます。

正しいバージョンでは、sequenceを使用してすべてのアクションを順番に実行し、対応する新しいもの(Element)をリストselectionList'に保持します。すべての要素が一度作成され、次に次に来るものにそれらを渡すだけです。結合子pureは、単に既存のものを返すアクションを構築します。それは動作します。最後に


#+のタイプは少し混乱することができるではなく、リスト[UI Element]のリスト[Element]を受け入れた場合、それはより透明であろう。後者は構文的なノイズを減らしますが、個々の要素に名前を付ける必要性が減ります。