4

Let's say I have

e1 :: Event t A
f  :: A -> IO B

I want to create

e2 :: Event t B

which is triggered by e1, and whose values are determined by executing f on the value of e1 at the time of the event occurrence.

I see two potential ways to do this, via dynamic event switching and using handlers, but they both look too complicated for such a simple thing.

What is the proper way to do this?

Roman Cheplyaka
  • 37,738
  • 7
  • 72
  • 121

2 Answers2

1

Since the function f has a side effect, this is actually not a simple thing to do. The main reason is that the order of side effects is not well-defined when there are multiple simultaneous events. More generally, I was unable to conceive a good semantics for dealing with IO actions in events. Consequently, reactive-banana does not provide a pure combinator for this situation.

If you want to do this anyway, you have to use a more elaborate mechanism that also determines the order of side effects. For instance, you can use reactimate and write a combinator

mapIO :: Frameworks t => (a -> IO b) -> Event t a -> Moment t (Event t b)
mapIO f e1 = do
    (e2, fire2) <- liftIO newAddHandler
    reactimate $ (\x -> f x >>= fire2) <$> e1
    fromAddHandler e2

However, note that this may give unexpected results as the result event e2 is no longer simultaneous with the input event e1. For instance, behaviors may have changed and other side effects may have been executed.

Heinrich Apfelmus
  • 11,034
  • 1
  • 39
  • 67
  • 1
    It is a bit surprising, given that the whole purpose of FRP is to deal with IO, that IO actions are not "first-class". In particular, simultaneous events do have ordering — so why not use the same ordering for the corresponding effects? I understand that this is not bullet-proof — e.g. an IO action can possibly trigger other events, or take too much time to regard events as "simultaneous"... But I can take a responsibility for that; that is, I promise not to invoke any event handlers and the action will be "instantaneous" for my purposes. Is it still impossible? Why? – Roman Cheplyaka Jun 24 '13 at 11:30
  • 1
    Well, simultaneous events may have an ordering in every event, but there is no global ordering. Consider the two events `union ex ey` and `union ey ex` where `ex` and `ey` have simultaneous occurrences. A program may happily use both combinations at the same time, but the order of simultaneous event occurrences will be different for each. More generally, I could not find a good semantics for ordering IO actions in events, hence reactive-banana does not support them. (I have updated my answer to emphasize this point.) – Heinrich Apfelmus Jun 24 '13 at 19:06
  • 2
    Concerning FRP in general, I hope this does not come as a disappointment, but the focus is on calculating with events and time-varying values and not so much on IO. That said, you often need to interface with IO at the boundary, and there are many possibilities: `reactimate`, or the `mapIO` combinator presented here, or the `mapIO` combinator in the `Reactive.Banana.Frameworks.AddHandler` module which can be used to modify events before passing them to the FRP world. – Heinrich Apfelmus Jun 24 '13 at 19:07
  • @HeinrichApfelmus, could you please give an example of how to use `mapIO` from `Reactive.Banana.Frameworks.AddHandler`? Or maybe you could point to a better solution: in wxHaskell `Selecting` fires non-parametric event, so in order to know what is actually selected, I intend to use such `mapIO` after the event occurrence. – sukhmel Jun 11 '14 at 08:11
  • @sukhmel Have a look at the [reactive-banana-wx source](https://github.com/HeinrichApfelmus/reactive-banana/blob/master/reactive-banana-wx/src/Reactive/Banana/WX.hs#L99). – Heinrich Apfelmus Jun 12 '14 at 09:57
0

Can you call function f in reactimate (which as far as I understand is the "proper way" to handle IO from the event network)? From there, fire a new event of type Event t B to the event network. Or is this what you meant by "using handlers"?

ipuustin
  • 108
  • 6