与传统的FRP框架相比,Netwire中的事件处理

Dmi*_*yal 9 events haskell frp reactive-banana

大多数Haskell FRP框架如AFRP,Yampa和Reactive-banana在连续时变函数和离散函数之间产生差异.通常他们称他们为行为和事件.

一个例外是Netwire,它使用抑制monoid来模拟事件.这种方法的优点和缺点是什么?

特别是,我对FRP在机器人控制中的应用感兴趣.例如,本文http://haskell.cs.yale.edu/?post_type=publication&p=182显示了一种使用事件对FRP中的任务和HSM抽象进行编码的方法.这可以直接翻译成Netwire吗?

ert*_*tes 10

事件作为潜在抑制信号的优点是它允许您非常简洁地编码大多数甚至复杂的反应式.想象一下,按下时显示"是"的开关,否则显示"否":

"yes" . switchPressed <|> "no"
Run Code Online (Sandbox Code Playgroud)

这个想法是,switchPressed如果相应的事件发生,它就像身份线一样,否则就会禁止.这就是<|>进来的地方.如果第一根电线禁止,它会尝试第二根电线.这是一个由两个按钮(左和右)控制的假想机器人手臂:

robotArm = integral_ 0 . direction
direction =
    ((-1) . leftPressed  <|> 0) +
    (1    . rightPressed <|> 0)
Run Code Online (Sandbox Code Playgroud)

虽然机器人手臂是假设的,但这个代码不是.这真的是你在Netwire中写这个的方式.


Dmi*_*yal 2

经过一些试验,我已经实现了我需要的行为。基本上,您编写一个自定义抑制剂类型来捕获您需要的事件的概念。就我而言是

data Inhibitor = Done | Timeout | Interrupt deriving Show
Run Code Online (Sandbox Code Playgroud)

Done 意味着正常完成,其余构造函数发出某种错误信号。

之后,您可以编写所需的任何自定义组合器。就我而言,我需要一种方法来停止计算并进一步发出错误信号:

timeout deadline w | deadline <= 0 = inhibit Timeout
                   | otherwise = mkGen $ \dt a -> do
                       res <- stepWire w dt a
                       case res of
                         (Right o, w') -> return (Right o, timeout (deadline - dt) w')
                         (Left e, _)  -> return (Left e, inhibit e)
Run Code Online (Sandbox Code Playgroud)

这是 switchBy 的一个变体,它允许您更改一次电线。注意,它传递了新线的抑制信号:

switchOn new w0 =
    mkGen $ \dt x' ->
        let select w' = do
                (mx, w) <- stepWire w' dt x'
                case mx of
                  Left ex -> stepWire (new ex) dt x'
                  Right x -> return (Right x, switchOn new w)
        in select w0
Run Code Online (Sandbox Code Playgroud)

这是 (-->) 的一个变体,它抓住了中断任务链的想法。

infixr 1 ~>

w1 ~> w2 = switchOn ( \e -> case e of
                         Done -> w2
                         _ -> inhibit e
                    ) w1
Run Code Online (Sandbox Code Playgroud)