
Daan Leijen wrote:
This mechanism is enough to build a nice framework around. For example, you could add a callback to the existing ones.
buttonAddOnClick io button = do prev <-buttonGetOnClick button buttonSetOnClick (do{ io; prev }) button
The problem arises when the completely unrelated code which installed the original callback want to remove it. The "daisy chain" approach only works if callbacks are removed in the reverse order to their installation.
The only think lacking is indeed the "unregister" call but I think it is a highly unusual thing to deinstall a callback without replacement on an existing widget.
Maybe on Windows, where you can just have the callback do nothing if a flag is set. On X, it's important to deregister callbacks (particularly for mouse motion) when they aren't required.
I disagree with this design. It is very awkward to install callbacks this way as we have to maintain the "unregister" function. We can no longer use "nice" syntax like:
button [on click := messageBox "hi there"]
You can have the "unregister onClick action" in the resulting "ButtonType" value, just as you keep there other important attributes of a button.
Not if you want to support multiple callbacks for each event. In that case, you need to be able to identify a specific callback, which gets us back to the issue of equality of function types. Unfortunately, the easiest solution (require the application to provide a FunPtr, as that's what's going to be passed to the lower levels) is also the ugliest.
2a) For every callback, there should be a register function. The register function should return an IO action that can be used to unregister the callback. There can be multiple callbacks for one event.
I'd like to provide an example of where I'd prefer to have 2a:
A more obvious example: libraries. Code which is passed a reference to a widget should be able to add and remove callbacks for that widget independently of any other code. Having a single callback necessitates global coordination. Axel Simon wrote:
I disagree with this design. It is very awkward to install callbacks this way as we have to maintain the "unregister" function. We can no longer use "nice" syntax like:
button [on click := messageBox "hi there"]
I think this is one of the mid-level features we want to keep: A convenient syntax, not just a bunch of IO functions like
buttonOnClick :: IO () -> Button -> IO () buttonGetOnClick :: Button -> IO (IO ())
The "convenient syntax" only makes sense if you view events and
callbacks as being in 1:1 correspondence. Not all toolkits take this
view. Xt has callback lists, where callbacks can be added and removed
independently (callbacks are identified by equality on function
pointers, which becomes problematic if you want to pass Haskell
functions directly); similarly for the signal/slot approach.
--
Glynn Clements