我想要将IO操作映射到行为上,但(threepenny-gui)中没有这样的函数,是否有某种方法可以使用公开的API构建它。它在语义上是合理的吗?
在我的用例中,我已经在threepenny中实现了这一点。
-- Just bind the IO action to the Latch
unsafeMapIOB :: (a → IO b) → Behavior a → Behavior b
unsafeMapIOB f (B l e) = B (Prim.bindL (Prim.Latch ∘ f) l) e
-- Wrap the IO bind
bindL :: (a -> Latch b) -> Latch a -> Latch b
bindL f l = Latch { readL = (readL ∘ f) =<< readL l}
-- Map the IO action over both Behavior and Event
unsafeMapIOT :: (a -> IO b) -> Tidings a -> Tidings b
unsafeMapIOT f x = tidings (unsafeMapIOB f $ facts x) (unsafeMapIO f $ rumors x)我从来没有单独使用过unsafeMapIOB,只使用过unsafeMapIOT。我认为正在发生的事情是,unsafeMapIOB只执行一次,然后用事件触发unsafeMapIO。
发布于 2014-08-15 06:42:29
对于理想的玻璃钢语义,行为是随时间变化的函数:Time → a-it可以随着时间的推移而不断变化。这意味着,在每个更改上调用IO操作在语义上并不合理,因为在语义中没有离散更改的概念。
这在实践中也是有意义的:取决于行为的值何时发生变化通常过于依赖于实现。例如,考虑鼠标位置:值更改的频率基于轮询的频率,这非常依赖于系统。即使实际行为不是连续的,离散的更改仍然是我们不想泄露到您的程序中的细节。(在理想情况下,也许这种行为也是连续的-也许系统会进行某种插值或平滑,以补偿硬件中的底层轮询。)
为了避免依赖于这些任意的实现细节,您必须明确说明何时对您的行为进行采样以触发IO操作。您可以通过获取另一个事件流的时间组件来实现这一点,创建一个新的事件流,其中包含同时发生的事件,但值基于行为的当前值。在反应式香蕉中,您可以使用<@操作符完成此操作。特定于我们的类型,我们得到:
(<@) :: Behavior a -> Event b -> Event a本质上,我们将行为的值映射到事件流上,从而得到一个新的事件流。然后,我们就可以使用一个标准函数在每个事件上触发一个IO操作。
最后一个问题是,你从哪里得到你的事件流,这将取决于你的特定程序。如果行为因某些特定的用户操作而发生更改,则可以获取该操作的流。您也可以只创建一个计时器事件,并在给定的时间间隔轮询。或者,您甚至可以使用union同时做这两件事!这是一个很好的例子,说明了FRP是如何非常可组合的。
https://stackoverflow.com/questions/25307118
复制相似问题