反应香蕉:触发事件,包含行为的最新值
Posted
技术标签:
【中文标题】反应香蕉:触发事件,包含行为的最新值【英文标题】:reactive-banana: Firing event that contain the most up to date value of a Behavior 【发布时间】:2012-01-26 22:22:40 【问题描述】:假设我有一个事件trigger,我想在触发时做两件事。首先,我希望它更新一些行为的价值。其次,如果满足其他条件,我希望它触发另一个具有 updated 行为值的事件 send_off。以代码形式表示,假设我有
trigger :: Event b
trigger = ...
updateFromTrigger :: b -> (a -> a)
updateFromTrigger = ...
conditionFromTrigger :: b -> Bool
conditionFromTrigger = ...
behavior :: Behavior a
behavior = accumB initial_value (updateFromTrigger <$> trigger)
send_off :: Event a
send_off = ?????? (filterE conditionFromTrigger trigger)
那么问题来了:我在??????以便 send_off 发送最新的 behavior 值,我的意思是包含来自 trigger 的更新的值应用到它。
不幸的是,如果我理解正确,行为的语义是更新的值不能立即提供给我,所以我在这里唯一的选择本质上是复制工作并重新计算更新的行为值,以便我可以立即在另一个事件中使用它,即填写??????像
send_off =
flip updateFromTrigger
<$>
behavior
<@>
filterE conditionFromTrigger trigger
现在,在某种意义上,我可以通过使用 Discrete 而不是 Behavior 立即使我可以使用行为中的更新信息,但这实际上相当于给我一个与具有更新值的原始事件同时触发的事件,除非我错过了某些东西,否则响应式香蕉不会给我一种方法来触发一个事件,只有当其他两个事件同时触发时;也就是说,它提供事件的并集,但不提供交集。
所以我有两个问题。首先,我对这种情况的理解是否正确,特别是我是否正确得出结论,我的上述解决方案是解决它的唯一方法?其次,纯粹出于好奇,开发者有没有关于如何处理事件交叉点的想法或计划?
【问题讨论】:
【参考方案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
,因为它感觉更自然。
reactive-banana 的下一个版本(> 0.4.3)可能会包含函数
collect :: Event a -> Event [a]
spread :: Event [a] -> Event a
具体化,resp。反映同时发生的事件。无论如何,我需要它们来优化 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
但是,根据上面的讨论,此功能可能没有您希望的那么有用。特别是它不是唯一的,有很多变种。
【讨论】:
@GregoryCrosswhite:你一直在问好问题。 ;-) 我已经为新的内部设计了一个漂亮的设计,但实施它需要一段时间。以上是关于反应香蕉:触发事件,包含行为的最新值的主要内容,如果未能解决你的问题,请参考以下文章