5

この小さなプログラムの目的は、3 つのボタンを表示することです。3 番目のボタンのラベルは最初は「0」で、その後は最後にクリックされたボタンのインデックスになります。今のところ、ボタンの数と他のボタンのラベルは一定です。

この自己完結型のファイルを ghcjs でコンパイルし、Main.jsexe/index.html をブラウザーにロードすると、2 つの traceDyns がループで起動し、両方とも値が常に 0 であることがわかります。私が理解している限り、何も起こらないはずです。 _el_clicked がシステムの残りの部分にフィードするため、ボタンがクリックされるまで。

また、mapDyn (fst . head . Map.toList)選択したボタンのインデックスを抽出するために使用していることに注意してください。これが正しいかどうかはわかりませんが、どちらにしても無限ループの原因はわかりません。

{-# LANGUAGE RecursiveDo #-}

module Main where

import Reflex
import Reflex.Dom

import qualified Data.Map as Map

dynButton
  :: MonadWidget t m
  => Dynamic t String
  -> m (Event t ())
dynButton s = do
  (e, _) <- el' "button" $ dynText s
  return $ _el_clicked e

-- widget that takes dynamic list of strings
-- and displays a button for each, returning
-- an event of chosen button's index
listChoiceWidget
  :: MonadWidget t m
  => Dynamic t [String]
  -> m (Event t Int)
listChoiceWidget choices = el "div" $ do
  asMap <- mapDyn (Map.fromList . zip [(0::Int)..]) choices
  evs <- listWithKey asMap (\_ s -> dynButton s)
  k <- mapDyn (fst . head . Map.toList) evs
  return $ updated (traceDyn "k" k)

options :: MonadWidget t m => Dynamic t Int -> m (Dynamic t [String])
options foo = do
  mapDyn (\x -> ["a", "b", show x]) foo

main :: IO ()
main = mainWidget $ el "div" $ do
  rec n <- listChoiceWidget o
      o <- options foo
      foo <- holdDyn 0 n
  display (traceDyn "foo" foo)
4

1 に答える 1

5

listChoiceWidget のコードが、dynButton によって作成されたクリック イベントを破棄しているようです。

listWithKey戻りますm (Dynamic t (Map k a))。あなたの場合、キーは型Intで、値はEvent t ()(dynButtonによって生成されます)です。

この行で:

k <- mapDyn (fst . head . Map.toList) evs

を に変えてDynamic t (Map Int (Event t ()))Dynamic t Intますが、重要なことに、クリックイベントが発生したときにそうしていません。evsこの行は、イベントが発生したかどうかに関係なく、Map of Ints to Events の最初のキーを常に含む Dynamic をマッピングして生成します。これは常に Int 0 を含む Dynamic になります。

ループが表示される理由は次のとおりです。

  1. mainfooに初期値 0 をフィードしますoptions
  2. 新しいオプションが構築されます
  3. listChoiceWidget新しいオプションを受け取り、リストが更新されます
  4. 結果として得られる Ints to Events の Map の最初のキーが更新されます
  5. fooキー更新イベントを受け取りますlistChoiceWidget
  6. ステップ 2 に無限に戻る

Map から最初のキーを取得する代わりに、最後のボタン クリック イベントを特定する何らかの方法が必要です。マップには、表示される各ボタンのクリック イベントが既に含まれています。現在、これらのイベントのタイプは ですが、Event t ()実際に必要なのは ですEvent t Int。これにより、イベントが発生したときに、それがどのボタンから来たかを知ることができます。

evs' <- mapDyn (Map.mapWithKey (\k e -> fmap (const k) e)) evs

evs'タイプを持っていDynamic t (Map Int (Event t Int))ます。次に、イベントを組み合わせて、最後にクリックされたボタンのキーで発生する 1 つのイベントを作成する方法が必要です。

dynEv <- mapDyn (leftmost . Map.elems . Map.mapWithKey (\k e -> fmap (const k) e)) evs

dynEvタイプが になりましたDynamic t (Event t Int)。マップのキーは既にイベントに焼き付けられているので、もう必要ありません。Map.elemsイベントのマップをイベントのリストに変換し、イベントleftmostのリストを 1 つのイベントに結合できるようにします。

ドキュメントからleftmost:「リスト内のイベントの少なくとも 1 つが発生した場合に発生する新しいイベントを作成します。同時に複数のイベントが発生した場合、指定された関数で左から折りたたまれます。」

最後に、あなたを に変換する必要がありDynamic t (Event t Int)ますEvent t Int。を使用switchして を受け取り、Behavior t (Event t a)を返しますEvent t a。したがって、次の行はEvent t Int.

switch (current dynEv)

currentの を抽出し、Behavior「現在選択されている入力イベントが発生するたびに発生するイベント」Dynamicを作成します。switch

これが修正されたlistChoiceWidgetコードです。インライン型注釈を含めたので、ScopedTypeVariablesこのコードをコンパイルするには言語拡張機能を有効にする必要があります (注釈を削除することもできます)。

listChoiceWidget
  :: forall t m. MonadWidget t m
  => Dynamic t [String]
  -> m (Event t Int)
listChoiceWidget choices = el "div" $ do
  asMap <- mapDyn (Map.fromList . zip [(0::Int)..]) choices
  evs :: Dynamic t (Map.Map Int (Event t ())) <- listWithKey asMap (\_ s -> dynButton s)
  dynEv :: Dynamic t (Event t Int) <- mapDyn (leftmost . Map.elems . Map.mapWithKey (\k e -> fmap (const k) e)) evs
  return $ switch (current dynEv)

これが完全なファイルの要点です。

于 2015-07-28T19:02:59.607 に答える