我是反应性香蕉和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
的事件)的事件中获取行为,并且我没有看到如何将函数应用于没有行为的事件。总的来说,在泛型类型的情况下,我没有看到如何为步进器提供初始值。
答案 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)
如果您确实需要同时拥有a
和b
,则相应的解决方案是创建一个Behavior
,累积在一个或另一个上,或合并{ {1}}如上所述。
编辑:在合并流上累积的一种方法的示例(未经测试)。其他实现也是可能的。这不会使两个事件同时发生,而是让您将当前状态与过去状态相结合,这样您就可以始终获得Either
和Left
值的最新状态。
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
的事件,那么元组的第二部分应该有什么价值呢?