What's wrong with my "sum event" in reactive-banana?
Asked Answered
K

1

7

I'm learning reactive-banana and intending to use it in a server-side app. I have some background in RxJs, so I'm used to combining Events with different combinators there. So I started with simple event combinator examples. I tried to make a simple reactive-banana example that would combine two integer events into a sum event. I understand that to be able to combine values from different events, I have to make them into Behaviors first, the do the combination and finally make that into a new event. This is how I did it:

-- Behaviors from Events e1, e2
let b1 = stepper 0 e1 :: Behavior Int
let b2 = stepper 0 e2 :: Behavior Int

-- Sum Behavior
let sumB = (+) <$> b1 <*> b2
-- Back to Event
let sumE = sumB <@ (e1 `union` e2)

Full runnable example can be found in Gist 1594917.

The problem here is that while the sumE event is correctly triggered when a new value appears in one of the events (e1, e2), it contains a stale value. This is apparently due to how stepper works (the value of the behavior changes "slightly after" the event occurrences). I tried replacing Behavior with Discrete, with the same result.

Is there a simple way to make this kind of event combinators work correctly?

Kappel answered 11/1, 2012 at 21:33 Comment(0)
P
5

Your diagnosis is exactly correct. Here are two options: you can work back to an Event from Discrete via changes, or you can create accumulating events.

Working back from Discrete is probably simpler (and is what I would recommend). Just do

-- Discretes from Events e1, e2
let d1 = stepperD 0 e1 :: Discrete Int
let d2 = stepperD 0 e2 :: Discrete Int

-- Sum Discrete
let sumD = (+) <$> d1 <*> d2
-- Back to Event
let sumE = changes sumD

Now sumE will always update when e1 or e2 changes.

The alternative uses only Events, by converting incoming events into accumulating functions. It sounds complex, but the code is fairly simple.

--convert each input into an accumulating tuple
let e1' = (\l (_,r) -> (l,r)) <$> e1
let e2' = (\r (l,_) -> (l,r)) <$> e2

let sumE = uncurry (+) <$> accumE (0,0) (e1' `union` e2')
Possum answered 12/1, 2012 at 11:19 Comment(2)
Just to clarify: there is no delay in either of your approaches, the sumE events will occur simultaneously with e1 and e2.Morna
Thanks, @HeinrichApfelmus. I've edited my answer to hopefully be more clear.Possum

© 2022 - 2024 — McMap. All rights reserved.