Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing zipE :: Event t a -> Event t b -> Event t (a,b)

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.

like image 654
copton Avatar asked Jul 31 '12 14:07

copton


1 Answers

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?

like image 191
John L Avatar answered Oct 10 '22 08:10

John L