
George Russell wrote:
Oh gosh I'm glad we've got a decent debate going on this ...
Wolfgang Thaller wrote (snipped)
1) Callbacks will be executed synchronously. No other events will be handled until a callback returns. Rationale: *) This maps directly to the execution model of all backend toolkits that I know *) You can easily get concurrency by calling forkIO, but going the other way is difficult.
I am strongly opposed to this proposal. As a matter of fact this does *not* map directly to the execution model of HTk, in the sense that if I wanted to add such callbacks to HTk I would have to add an extra lock to prevent callbacks running synchronously. However there is a more fundamental objection.
What do you mean by "callbacks running synchronously"? Let's assume that, in a button callback, I do some calculation that takes about one or two seconds, and then show a dialog box. To me, "asynchronous callbacks" would mean that I could click other buttons and trigger other actions before the dialog shows. I don't know how to prevent that if I don't have a guarantee that callbacks are synchronous. Going the other way is easy - just wrap the callback handler in a call to forkIO.
I think in general this does not scale. For example initialisation code (1) might occasionally need to put up a window (eg one that says "Can't find configuration file, please enter new path") (2) might, thanks to the common paradigm of doing initialisation using unsafePerformIO, need to be run almost anywhere in the program, including during a callback. The only way of avoiding that is to force the user to adopt a paradigm where no callback may make use of a service which might require an initialisation (or anything else) which itself requires a GUI event. This is highly unpleasant.
I think invoking a dialog from unsafePerformIO is highly unpleasant. But I think you are misunderstanding my proposal. A callback may, of course, start a "nested" event loop for handling a modal dialog. You can use all the concurrency you want by wrapping your handlers in forkIO (if my proposal 2b, or a variant thereof, is accepted). I'll try to rephrase my proposal 1: 1) The event loop will not invoke a callback action until the previous callback action has returned, i.e. callbacks will be invoked sequentially, not concurrently. 1a) Event loops may be nested, i.e. a callback, or (if proposal 2 is also accepted), any other IO action, may call a CGA function that runs another event loop, e.g. for handling an application-modal dialog (the main event loop won't handle events during that time).
Also this way of programming is highly unpleasant. For example, if I have a button "Send an insult to Simon.Peyton-Jones@microsoft.com" and it wants to put up a window saying "Do you really want to insult Simon Peyton-Jones?" then you need a chain of callbacks. The callback attached to the first button is not allowed to insult Simon PJ itself, instead it has to attach a continuation (containing the insult) to the confirmation button. In general this means continuation-passing-style is going to infect this part of the program. For programs where almost everything is initiated by user action, an awful lot of them is going to have to be written in CPS.
I think Haskell's type system already makes it illegal to insult Simon PJ (at least in GHC) ;-). Seriously, as I said, I was not talking about forcing people to use CPS. The intent is to allow, but to NOT require concurrency. In other words, my proposal 1) refers to the same event handling model as used by GTK, Carbon, and Win32 (with the exception that Win32 uses an explicit event loop, while the others have RunApplicationEventLoop() and gtk_whatsitcalled). Proposal 2) (especially 2b) talks about extending this to support all concurrency you could possibly ever want, without forcing concurrency on everybody. Cheers, Wolfgang