Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Event handling in Netwire compared to conventional FRP frameworks

Most Haskell FRP frameworks like AFRP, Yampa and Reactive-banana make a difference between continuous time-varying functions and discrete ones. Usually they call them behaviors and events.

One exception is Netwire, which uses an inhibition monoid to model events. What are pros and cons of such an approach?

In particular, I'm interested in application of FRP to robot controlling. For example, this paper http://haskell.cs.yale.edu/?post_type=publication&p=182 show a way to encode a task and HSM abstractions in FRP using events. Can this be directly translated to Netwire?

like image 968
Dmitry Vyal Avatar asked Feb 01 '13 20:02

Dmitry Vyal


2 Answers

The advantage of events as potentially inhibited signals is that it allows you to encode most even complicated reactive formulas very concisely. Imagine a switch that displays "yes" when pressed and "no" otherwise:

"yes" . switchPressed <|> "no"

The idea is that switchPressed acts like the identity wire if its corresponding event occurs and inhibits otherwise. That's where <|> comes in. If the first wire inhibits, it tries the second. Here is a hypothetical robot arm controlled by two buttons (left and right):

robotArm = integral_ 0 . direction
direction =
    ((-1) . leftPressed  <|> 0) +
    (1    . rightPressed <|> 0)

While the robot arm is hypothetical, this code is not. It's really the way you would write this in Netwire.

like image 178
ertes Avatar answered Oct 23 '22 19:10

ertes


After some trials I've implemented the behavior I needed. Basically, You write a custom inhibitor type which catches the concept of events you need. In my case it was

data Inhibitor = Done | Timeout | Interrupt deriving Show

Done means normal finishing and the rest constructors signal some kind of an error.

After it, you write any custom combinators you need. In my case I needed a way to stop computations and signal a error further:

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)

This is a variant of switchBy which allows you to change the wire once. Note, it passes the inhibition signal of a new wire:

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

And this is a variant of (-->) which catches the idea of interrupting the task chain.

infixr 1 ~>

w1 ~> w2 = switchOn ( \e -> case e of
                         Done -> w2
                         _ -> inhibit e
                    ) w1
like image 21
Dmitry Vyal Avatar answered Oct 23 '22 18:10

Dmitry Vyal