Ответ 1
Отличный вопрос!
К сожалению, я думаю, что здесь есть фундаментальная проблема, которая не имеет простого решения. Проблема заключается в следующем: вы хотите получить самое последнее накопленное значение, но trigger
может содержать одновременно происходящие события (которые все еще упорядочены). Тогда,
Какое из одновременных обновлений аккумулятора будет самым последним?
Дело в том, что обновления упорядочены в потоке событий, к которому они принадлежат, но не относятся к другим потокам событий. Используемая здесь FRP-семантика больше не знает, какое одновременное обновление для behavior
соответствует одновременному событию send_off
. В частности, это показывает, что ваша предлагаемая реализация для send_off
, скорее всего, неверна; он не работает, когда trigger
содержит одновременные события, потому что поведение может обновляться несколько раз, но вы только пересчитываете обновление один раз.
Имея это в виду, я могу придумать несколько подходов к проблеме:
-
Используйте
mapAccum
, чтобы аннотировать каждое событие триггера новым обновленным значением аккумулятора.(trigger', behavior) = mapAccum initial_value $ f <$> trigger where f x acc = (x, updateFromTrigger acc) send_off = fmap snd . filterE (conditionFromTrigger . fst) $ trigger'
Я думаю, что это решение немного не отличается от модульности, но в свете вышеизложенного этого, вероятно, трудно избежать.
-
Повторите все с точки зрения
Discrete
.У меня нет никаких конкретных предложений здесь, но может случиться так, что ваше событие
send_off
больше похоже на обновление на значение, чем на правильное событие. В этом случае, возможно, стоит бросить все в терминахDiscrete
, чей экземплярApplicative
делает "правильную вещь", когда происходят одновременные события.В подобном духе я часто использую
changes . accumD
вместоaccumE
, потому что он чувствует себя более естественным. -
Следующая версия реактивного банана ( > 0.4.3) скорее всего будет включать функции
collect :: Event a -> Event [a] spread :: Event [a] -> Event a
которые подтверждают, соответственно. отражают одновременные события. Мне нужно, чтобы они все равно оптимизировали тип
Discrete
, но они, вероятно, полезны для таких вещей, как настоящий вопрос.В частности, они позволят вам определить пересечение событий таким образом:
intersect :: Event a -> Event b -> Event (a,b) intersect e1 e2 = spread . fmap f . collect $ (Left <$> e1) `union` (Right <$> e2) where f xs = zipWith (\(Left x) (Right y) -> (x,y)) left right where (left, right) = span isLeft xs
Однако, в свете вышеизложенного, эта функция может быть менее полезной, чем вы хотели бы. В частности, это не уникально, есть много вариантов.