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

cop*_*ton 7 haskell frp reactive-programming reactive-banana

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

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

其中之一是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
Run Code Online (Sandbox Code Playgroud)

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

其他尝试失败,例如

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

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

Joh*_*n L 13

你的定义有困难,zipE因为它没有语义意义.如果考虑语义定义

Event a == [(Time, a)]
Event b == [(Time, b)]
Run Code Online (Sandbox Code Playgroud)

没有一种自然的方式来拉链它们,因为通常每个事件都会在不同的时间.

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

zipE :: Event a -> Event b -> Event (Either a b)
zipE aE bE = (Left <$> aE) `mappend` (Right <$> bE)
Run Code Online (Sandbox Code Playgroud)

如果你真的需要有两个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)
Run Code Online (Sandbox Code Playgroud)

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