7

一般来说,我对反应性香蕉和 FRP 很陌生,所以如果我遗漏了一些明显的东西,我深表歉意。

对于我的项目(一个GDB/MI前端),我对 GUI 和前端逻辑模块都使用了响应式香蕉(版本 0.6.0.0)。前者效果很好,但对于后者,我显然需要额外的组合器。

其中之一是zipE :: Event t a -> Event t b -> Event t (a, b)。不幸的是,我能想出的只是 NetworkDescription monad 中的一个解决方案,它changes在事件类型中使用并且不是通用的:

zipE :: Event t Int -> Event t String -> NetworkDescription t (Event t (Int, String))
zipE ea eb = changes $ (,) <$> stepper 0 ea <*> stepper "" eb

当然,我对此并不满意。因此,我想问如何在不使用的情况下实现一个通用的 zipE 函数changes(不鼓励将其用于非 GUI 目的)。

其他尝试失败,例如

zipE :: Num a => Event t a -> Event t b -> Event t (a,b)
zipE ea eb = apply (stepper (0,) ((,) <$> ea)) eb

导致元组的第一个元素被移动一个 - 我猜是由于stepper. 但是我看不到如何从没有stepper(或accumB就此而言)的事件中获取行为,也看不到如何将函数应用于没有行为的事件。总的来说,在泛型类型的情况下,我看不到如何为步进器提供初始值。

4

1 回答 1

13

你很难定义zipE,因为它没有语义意义。如果您考虑语义定义

Event a == [(Time, a)]
Event b == [(Time, b)]

没有一种自然的方式来压缩它们,因为通常每个事件都会在不同的时间进行。

可以使用总和而不是产品来压缩它们。

zipE :: Event a -> Event b -> Event (Either a b)
zipE aE bE = (Left <$> aE) `mappend` (Right <$> bE)

如果您确实需要同时拥有两者ab则适当的解决方案是创建一个Behavior累积在一个或另一个上的,或Either如上所述的合并流。

编辑:在合并流上累积的一种方法示例(未经测试)。其他实现也是可能的。这不会使这两个事件同时发生,而是让您将当前状态与过去状态结合起来,这样您就可以始终拥有 和 值中最近可用的LeftRight

currentAandB :: a -> b -> Event a -> Event b -> Event (a,b)
currentAandB a0 b0 aE bE = accumE (a0,b0) (mergefn <$> zipE aE bE)
    where
        mergefn (Left a)  (_,b) = (a,b)
        mergefn (Right b) (a,_) = (a,b)

仍然需要为两个Event流提供初始值。这样想:如果你只有来自 的事件Event a,那么元组的第二部分应该有什么值?

于 2012-07-31T15:01:08.117 回答