实现zipE :: Event t a - >事件t b - >事件t(a,b)

时间:2012-07-31 14:37:33

标签: haskell reactive-programming frp reactive-banana

我是反应性香蕉和FRP的新手,所以如果我遗漏了一些明显的东西,我会道歉。

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

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

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

当然,我对此并不满意。因此,我想问一下如何在不使用changes的情况下实现通用zipE函数(不鼓励将其用于非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的事件)的事件中获取行为,并且我没有看到如何将函数应用于没有行为的事件。总的来说,在泛型类型的情况下,我没有看到如何为步进器提供初始值。

1 个答案:

答案 0 :(得分: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,累积在一个或另一个上,或合并{ {1}}如上所述。

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

Right

仍然需要为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的事件,那么元组的第二部分应该有什么价值呢?