reactive-banana:包含行为的最新值的触发事件 [英] reactive-banana: Firing event that contain the most up to date value of a Behavior

查看:144
本文介绍了reactive-banana:包含行为的最新值的触发事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个事件触发器,我想在触发时做两件事情。首先,我希望它更新一些行为的值。其次,如果满足其他条件,我希望它使用行为的更新值触发另一个事件 send_off 。用代码形式表示,假设我有

  trigger:事件b 
trigger = ...

updateFromTrigger :: b - > (a - > a)
updateFromTrigger = ...

conditionFromTrigger :: b - > Bool
conditionFromTrigger = ...

行为::行为a
行为= accumB initial_value(updateFromTrigger< $>触发器)

send_off: :事件a
send_off = ?????? (filterE conditionFromTrigger trigger)

然后问题是:我在?????中输入了什么? ?以便 send_off 发送最新的行为值,我的意思是包含触发更新的值只是应用到它。



不幸的是,如果我理解正确,行为的语义是这样的,更新后的值不是立即可用的,所以我唯一的选择是基本上是为了复制工作并重新计算行为的更新值,以便我可以在另一个事件中立即使用它,即填写??????类似于

  send_off = 
翻转updateFromTrigger
< $>
行为
< @>
filterE conditionFromTrigger trigger

现在,我可以知道 / em>通过使用Discrete而不是Behavior来更新行为中的信息,但实际上,这仅仅等同于给我一个与我的原始事件同时触发的事件和更新后的值,除非我错过了一些反应 - 香蕉不会给我一种只有在另外两个事件同时发射时才发射事件的方式;也就是说,它提供了事件的联合而不是交叉点。



所以我有两个问题。首先,我对这种情况的理解是否正确,特别是我的结论是否正确:我的解决方案是解决问题的唯一方法?其次,纯粹出于好奇心,开发者是否有过关于如何处理事件交集的想法或计划? 解决方案

>非常好的问题!



不幸的是,我认为在这里存在一个根本性问题,并没有简单的解决方案。问题如下:你需要最近的累计值,但是 trigger 可能同时包含事件(仍然是有序的)。然后,


哪个同时累加器更新将是最近的?

关键在于更新是在它们所属的事件流中排序的,而不是与其他事件流相关的。此处使用的FRP语义不再知道对行为的哪一个同时更新对应于哪一个同时发生 send_off 事件。特别是,这表明您对 send_off 的提议实现可能不正确;当触发包含同时发生的事件时它不起作用,因为行为可能会多次更新,但您只重新计算一次。



考虑到这一点,我可以想到几种解决问题的方法:


  1. 使用 mapAccum 来使用新更新的累加器值对每个触发事件进行注释。

     (trigger',行为)= mapAccum initial_value $ f< $>触发
    ,其中
    f x acc =(x,updateFromTrigger acc)

    send_off = fmap snd。 filterE(conditionFromTrigger.fst)$ trigger'

    我认为这个解决方案缺乏一点模块化,但根据上面的讨论,这可能是难以避免的。

  2. 使用离散

    这里我没有任何具体的建议,但可能是你的 send_off 事件感觉更像是一次价值更新,而不是像一个适当的事件。在这种情况下,根据 Discrete ,其 Applicative 实例可以执行正确的事情 当发生同时事件时。

    以类似的精神,我经常使用变化。 accumD 而不是 accumE ,因为它感觉更自然。 下一步反应式香蕉版本(> 0.4.3)可能包括功能

      collect :: Event a  - >事件[a] 
    spread :: Event [a] - >事件a

    即reify,resp。反映同时事件。我需要它们优化 Discrete 类型,但它们可能也适用于类似于当前问题的内容。



    特别是,它们允许你定义事件的交集:

      intersect :: Event a  - >事件b  - >事件(a,b)
    与e1 e2
    = spread相交。 fmap f。收取
    $(Left< $> e1)`union`(Right< $> e2)
    其中
    f xs = zipWith(\(Left x) - >(x,y))left right
    where(left,right)= span isLeft xs

    然而,根据上面的讨论,这个函数可能没有你想要的那么有用。特别是,它不是唯一的,有很多变体。


Suppose I have an event trigger which I want to do two things when fired. First, I want it to update the value of some behavior. Second, if other conditions are met, I want it to fire another event send_off with the updated value of the behavior. Expressed in code form, suppose I have

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)

Then the question is: what do I put in the ?????? so that send_off sends the most up to date value of behavior, by which I mean that the value which includes the update from trigger that was just applied to it.

Unfortunately, if I understand correctly, the semantics of Behavior are such that the updated value isn't immediately available to me, so my only option here is essentially to duplicate the work and recompute the updated value of behavior so that I can use it immediately in another event, i.e. to fill in the ?????? with something like

send_off =
    flip updateFromTrigger
    <$>
    behavior
    <@>
    filterE conditionFromTrigger trigger

Now, there is a sense in which I can make the updated information in the behavior available to me right away by using a Discrete instead of a Behavior, but really this is just equivalent to giving me an event that is fired simultaneously with my original event with the updated value, and unless I have missed something reactive-banana doesn't give me a way to fire an event only when two other events have fired simultaneously; that is, it provides unions of events but not intersections.

So I have two questions. First, is my understanding of this situation correct, and in particular am I correct in conclusion that my solution above is the only way to work around it? Second, purely out of curiosity, have there been any thoughts or plans by the developers on how to deal with intersections of events?

解决方案

Excellent question!

Unfortunately, I think that there is fundamental problem here that has no easy solution. The problem is the following: you desire the most recent accumulated value, but trigger may contain simultaneously occuring events (that are still ordered). Then,

Which of the simultaneous accumulator updates is going to be the most recent?

The point is that the updates are ordered in the event stream they belong to, but not in relation to other event streams. The FRP semantics used here no longer know which simultaneous update to the behavior corresponds to which simultaneous send_off event. In particular, this shows that your proposed implementation for send_off is likely incorrect; it doesn't work when trigger contains simultaneous events because the behavior may be updated multiple times, but you're only recalculating the update once.

With this in mind, I can think of several approaches to the problem:

  1. Use mapAccum to annotate each trigger event with the newly updated accumulator value.

    (trigger', behavior) = mapAccum initial_value $ f <$> trigger
        where
        f x acc = (x, updateFromTrigger acc)
    
    send_off = fmap snd . filterE (conditionFromTrigger . fst) $ trigger'
    

    I think that this solution is lacking a bit in terms of modularity, but in light of the discussion above, this is probably hard to avoid.

  2. Recast everything in terms of Discrete.

    I don't have any concrete suggestion here, but it may be that your send_off event feels more like an update to a value than like a proper event. In that case, it may be worth to cast everything in terms of Discrete, whose Applicative instance does "the right thing" when simultaneous events occur.

    In a similar spirit, I often use changes . accumD instead of accumE because it feels more natural.

  3. The next version of reactive-banana (> 0.4.3) will likely include functions

    collect :: Event a   -> Event [a]
    spread  :: Event [a] -> Event a
    

    that reify, resp. reflect simultaneous events. I need them to optimize Discrete type anyway, but they are probably useful for stuff like the present question as well.

    In particular, they would allow you to define the intersection of events thusly:

    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 
    

    However, in light of the discussion above, this function may be less useful than you'd like it to be. In particular, it's not unique, there are many variants.

这篇关于reactive-banana:包含行为的最新值的触发事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆