Почему этот код Reflex приводит к тому, что Dynamics запускается бесконечно с одним и тем же значением?
Цель этой маленькой программы - показать три кнопки, причем метка третьей кнопки изначально была "0", а затем индексом последней нажатой кнопки. На данный момент количество кнопок и метки других кнопок постоянны.
Когда я компилирую этот автономный файл с помощью ghcjs и загружаю Main.jsexe/index.html в браузер, я вижу, как два 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)
1 ответ
Похоже, ваш код для 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
и генерирует динамику, которая всегда будет содержать первый ключ в карте целых чисел событий, независимо от того, было ли событие запущено или нет. Это всегда будет Dynamic, содержащий Int 0.
Причина, по которой вы видите цикл, заключается в следующем:
main
кормаfoo
с его начальным значением 0 вoptions
- новые варианты построены
listChoiceWidget
получает новые опции и список обновляется- первый ключ результирующей карты Ints to Events обновлен
foo
получает ключ обновленного события отlistChoiceWidget
- вернуться к шагу 2 до бесконечности
Вместо того, чтобы извлекать первый ключ из карты, вам нужен какой-то способ определения последнего нажатия кнопки. Ваша карта уже содержит события нажатия для каждой отображаемой кнопки. Прямо сейчас эти события имеют тип Event t ()
, но то, что вам действительно нужно, Event t Int
, чтобы при возникновении события вы могли определить, с какой кнопки оно пришло.
evs' <- mapDyn (Map.mapWithKey (\k e -> fmap (const k) e)) evs
evs'
имеет тип Dynamic t (Map Int (Event t Int))
, Затем нам нужно каким-то образом объединить наши события, чтобы у нас было одно событие, которое срабатывает при нажатии кнопки, которая была нажата последней.
dynEv <- mapDyn (leftmost . Map.elems . Map.mapWithKey (\k e -> fmap (const k) e)) evs
dynEv
теперь имеет тип Dynamic t (Event t Int)
, Ключи Карты уже были запечатлены в событиях, поэтому они нам больше не нужны. Map.elems
превращает нашу карту событий в список событий, и leftmost
позволяет объединить список событий в одно событие.
Из документов для leftmost
: "Создать новое событие, которое происходит, если происходит хотя бы одно из событий в списке. Если несколько событий происходит одновременно, они складываются слева с помощью данной функции".
Наконец, нам нужно конвертировать ваши 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)