14

I'm currently trying to get to know FRP through Heinrich Apfelmus' reactive-banana, which seems to be a quite well documented and simple library, compared to the other ones I've looked at.

However, I can't wrap my head around the AddHandler type. Let's say I want to use GLFW to get mouse button clicks so that I have something like eMouseButton :: Event (). Looking at the examples, it seems that I'd somehow have to use fromAddHandler, but I have no idea how to assemble that AddHandler argument. I think I'll have to use newAddHandler somehow, but how?

I guess an example of how to connect reactive-banana to something other than wx would help a lot.

HaskellElephant
  • 9,819
  • 4
  • 38
  • 67
bzn
  • 2,362
  • 1
  • 17
  • 20

1 Answers1

13

newAddHandler is used like this:

do (addHandler, fire) <- newAddHandler
   ...

addHandler is the AddHandler to pass to reactive-banana, and fire is a function of type a -> IO () (where a is your event type) that triggers the event.

For instance, you would likely install fire as the callback to GLFW's mouse-button event, like so:

registerMouseButton :: IO (Event MouseButton)
registerMouseButton = do
  (addHandler, fire) <- newAddHandler
  setMouseButtonCallback $ \button _ -> fire button
  fromAddHandler addHandler

(I'm not experienced with GLFW, so I'm not sure what the second argument to setMouseButtonCallback's callback is — if it's important, you'll need to amend this implementation appropriately.)

An AddHandler is just a function which takes a callback — a -> IO () — and registers it for the event in question; it then returns (from within IO) an IO () action used to deregister this handler, making the complete definition of AddHandler read as follows:

type AddHandler a = (a -> IO ()) -> IO (IO ())

So where does newAddHandler come in? Simple: newAddHandler maintains a list of handlers for an event, and activates them when fire x is executed.

You don't need newAddHandler if, like GTK+ and many other common toolkits, your toolkit already has facilities to register and deregister multiple event handlers; if it does, you should write your own implementation of an AddHandler. But if all it supports is a single callback, you should use newAddHandler.

Note that you never need to expose AddHandlers to the FRP-using code itself; they're just internal glue used to create Events from external inputs.

ehird
  • 40,602
  • 3
  • 180
  • 182
  • Finally it clicked! Thank you. – bzn Dec 25 '11 at 22:26
  • Oops — the last time I used reactive-banana with a GUI toolkit, I wrote a short (~30 line) binding to GTK+, which does have this functionality; I haven't really used wxWidgets, but assumed that it did too, because implementing an `AddHandler` for GTK+ was so natural :) Upon further investigation, it doesn't; I've corrected my answer. – ehird Dec 25 '11 at 22:37
  • @bzn: Basically, wxWidgets didn't offer a way to deregister specific event handlers again, that's why I had to use the `newAddHandler` utility. – Heinrich Apfelmus Dec 26 '11 at 08:55
  • Can somebody help me understand what newAddHandler is doing under the hood? may be the author @HeinrichApfelmus? i don't quiet understand the `let register handler` construction inside the do block, I can't figure out how the 'handler' is smuggle inside the let / do block, have kind of intuition that it comes from "outside" through the call of `register addHandler putStrLn` then it fires all the newAddHandler code responsible for building addHandler a (all names are taken from original library) -> may it a question for a new thread in Stack? – user3680029 Nov 28 '19 at 11:23
  • I think I made it - a confirmation would be nice. Thanks to Haskell reference transparency so, `addHandler _is_ AddHandler register` then `register _something_ **pattern match with** register _handler_ and _handler_ **becomes** _something_ (i.e. putStrLn if you stay with the library example). It's coming from outside as in my first thought. – user3680029 Nov 29 '19 at 14:55