【发布时间】:2017-05-21 11:01:04
【问题描述】:
当我想在响应式香蕉中使用动态输入时,我通常会设想一个大致如下工作的系统:
data InputSpecification -- Some data structure that specifies the inputs
-- which should be active right now
data MyInterestingData -- Some data that is relevant to my business logic
-- and is gathered through the dynamic inputs.
emptyData :: MyInterestingData
emptyData = -- Some initial MyInterestingData
setupDynamicInputs :: Event (InputSpecification) -> MomentIO (Behavior MyInterestingData)
setupDynamicInputs specE = do
newBehavior <- execute $ updateDynamicInputs <$> specE
switchB emptyData newBehavior
updateDynamicInputs :: InputSpecification -> MomentIO (Behavior MyInterestingData)
updateDynamicInputs = -- Here the dynamic inputs are set up according to
-- the specification and set up to update the returned
-- Behavior
这很好用,每当触发新的InputSpecification 时,输入都会更新。
我经常遇到的一个问题是我的InputSpecification 不是Event,而是Behavior InputSpecification(可能是因为我需要Applicative 组合器来构造它)。上述方法不起作用,因为execute 和switchB 不能用于Behaviors。
作为一个简单的解决方案,我可以使用reactive-banana documentation 中的这个函数:
plainChanges :: Behavior a -> MomentIO (Event a) plainChanges b = do (e, handle) <- newEvent eb <- changes b reactimate' $ (fmap handle) <$> eb return e
那么我可以在从plainChanges 获得的Event 上使用setupDynamicInputs,但是:
但是,不推荐这种方法[...]
所以我有点不愿意使用这种方法。
当规范保存在Behavior 中时,是否有一种“更简洁”的方法可以使我的输入与规范保持同步?
编辑
正如 Heinrich Apfelmus 在他的回答中指出的那样,我最初的问题的解决方案是不要使用 Behavior 来更新 InputSpecification。虽然我可以理解其背后的原因,但它并不能解决我遇到的问题,所以我将尝试解释为什么我想在这里使用Behavior。
只要输入由单个输入指定,通过Event 更新输入就很容易。例如,如果动态输入由一系列输入组成,则这些输入的规范将只是一个非负整数,表示应显示多少输入。
一旦通过多个输入获得输入规范,它就会变得更加复杂。例如,假设我们的InputSpecification 变为(Word, Word) 并指定具有给定尺寸的输入网格。如果我通过两个不同的输入获得这些维度,我将不得不将两个Event Words 合并为一个Event (Word, Word),这对于Events 来说并不是一项简单的任务,因为它们没有Applicative 实例就像Behaviors 一样。这就是为什么我通常喜欢在这种情况下使用Behaviors,但正如之前所讨论的,当你真正想要创建输入时,它们并没有让你更进一步。因此,如果 Behaviors 在这里不是正确的解决方案,而 Events 往往很难(或者在最坏的情况下不可能)组合起来,那么这个问题的正确解决方案是什么?
【问题讨论】: