I am new to reactive banana and FRP in general, so apologies if I am missing something obvious.
For my project (a GDB/MI front-end) I am using reactive banana (version 0.6.0.0) for both the GUI and the front-end logic modules. The former works great but for the latter I apparently need additional combinators.
One of them is zipE :: Event t a -> Event t b -> Event t (a, b)
. Unfortunately, all I could come up with is a solution in the NetworkDescription monad that uses changes
and is not generic in the event types:
zipE :: Event t Int -> Event t String -> NetworkDescription t (Event t (Int, String))
zipE ea eb = changes $ (,) <$> stepper 0 ea <*> stepper "" eb
Of course, I am not satisfied with this. Thus I wanted to ask how to implement a generic zipE function without using changes
(which is discouraged to use for non-GUI purposes).
Other attempts failed, e.g.
zipE :: Num a => Event t a -> Event t b -> Event t (a,b)
zipE ea eb = apply (stepper (0,) ((,) <$> ea)) eb
results in the first elements of the tuples being shifted by one - I guess due to the "slight delay" introduced by stepper
. But I don't see how to obtain a behavior from an event without stepper
(or accumB
for that matter) and I don't see how to apply a function to an event without a behavior. And overall, I don't see how to provide an initial value to stepper in case of a generic type.
You're having difficulty defining zipE
because it doesn't make semantic sense. If you consider the semantic definitions
Event a == [(Time, a)]
Event b == [(Time, b)]
there isn't a natural way to zip them, because in general each event will be at a different time.
It is possible to zip them using a sum instead of a product.
zipE :: Event a -> Event b -> Event (Either a b)
zipE aE bE = (Left <$> aE) `mappend` (Right <$> bE)
If you really need to have both a
and b
at the same time, the appropriate solution is to create a Behavior
that accumulates over one or the other, or the merged Either
stream as above.
edit: an example of one way to accumulate over the merged stream (untested). Other implementations are also possible. This doesn't make both events occur at the same time, rather it lets you combine the current state with past states so you can always have the most recently available of both Left
and Right
values.
currentAandB :: a -> b -> Event a -> Event b -> Event (a,b)
currentAandB a0 b0 aE bE = accumE (a0,b0) (mergefn <$> zipE aE bE)
where
mergefn (Left a) (_,b) = (a,b)
mergefn (Right b) (a,_) = (a,b)
It's still necessary to provide initial values for both Event
streams. Think of it this way: if you've only had events from Event a
, what value should the second part of the tuple have?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With