6

I'm looking for something like flatten :: Event [a] -> Event a (swap [] with Foldable f => f if you want) which would generate a separate event for each a in an Event's list, like split in an old version of sodium.

I suspect that this is somehow possible with switchE, but then I'd need a function of type [a] -> Event a.

I could possibly craft that myself with newEvent, but is there a function built-in to reactive-banana?

Edit:

Actually, I'm not so sure I can implement that with newEvent after all.

flatten :: Foldable f => f a -> Banana.MomentIO (Banana.Event a)
flatten xs = do
  (event, fire) <- Banana.newEvent
  liftIO $ forkIO $ mapM_ fire xs
  return event 

Will fire block until there are subscribers or will it just return immediately if there aren't any?

Edit 2:

Looking at the implementation of newAddHandler my implementation above won't work, because all events are possibly fired before any handlers can register.

Sebastian Graf
  • 3,602
  • 3
  • 27
  • 38

1 Answers1

6

This appears to be impossible. According to the notes in Heinrich Apfelmus's blog, Event doesn't support simultaneous occurrences. This is a relatively recent change; the post is dated August last year and v1.0 was released in October. It certainly wasn't the case when I originally learned Reactive Banana a few years ago.

But Event [a] seems like a reasonable way to represent a set of coincidental events in the first place. Why do you need to flatten it?

Benjamin Hodgson
  • 42,952
  • 15
  • 108
  • 157
  • I want to perform an `IO` action for each one of those `a`s (fetch/clone git repositories) and then execute downstream code after each one (e.g. build and benchmark the project). I'd prefer to do it in a 'depth-first' (running corresponing fetch and build consecutive) rather than 'breadth-first' manner (running all fetches, then running all builds). There are also other event sources which produce single `a`s, but I could wrap that in singleton lists of course. – Sebastian Graf Apr 29 '16 at 18:05
  • Actually, I think doing `IO` is the only thing where it matters semantically. – Sebastian Graf Apr 29 '16 at 18:06
  • I think `fmap (traverse (clone >=> build)) :: Event [Project] -> Event (IO ())` would do that, no? – Benjamin Hodgson Apr 29 '16 at 18:57
  • Yeah, but I'd rather separate `clone` and `build` into their own events/arrows, because `build` can be also triggered by other events. But thanks for the suggestions, I'll go try something. – Sebastian Graf Apr 29 '16 at 19:13
  • Just make `clone` and `build` into separate functions , as I suggested above, and `fmap (traverse build)` over the other events that could trigger a build – Benjamin Hodgson Apr 29 '16 at 23:31
  • I'll run with that! Although that leaves me a little puzzled wrt. how I should structure my application. Originally I thought I could just have many modules exporting `Event a -> MomentIO (Event b)` arrows hiding IO and state in a well-behaved way. Now I have to 'break' that rule with `build`, although it seems OK because it is a sink in the network anyway. – Sebastian Graf Apr 30 '16 at 07:57
  • 1
    *Event doesn't support simultaneous occurrences.* This is also the case for more recent versions of sodium, by the way. – Heinrich Apfelmus Apr 30 '16 at 08:05
  • @Sebastian That's not how I'd structure the code. Confine your use of the `Reactive.Banana.Frameworks` module to the entry point of your application, which should just consist of calls to `fromAddHandler` (to bind inputs) and `reactimate` (to bind outputs). Keeping `MomentIO` out of your library modules promotes composability. – Benjamin Hodgson Apr 30 '16 at 08:19
  • Thanks for your suggestions. I got the network to do what I want now. – Sebastian Graf May 01 '16 at 20:26