[GUI] GUIs and events (was: Re: Concurrency)

Wolfgang Thaller wolfgang.thaller@gmx.net
Wed, 12 Mar 2003 19:05:51 +0100

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 
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.