
Dear Haskellers, After a long absence I again have the chance to follow the discussions in the forums. And now I would like to express my personal opinion. A lot of attention is paid to the way of realizing the multithreading in CGA. I totally agree that the choice of concrete abstraction for multithreading is a little bit out of the CGA standard. The callback functions are simple and effective way of realizing the event handling. This mechanism doesn�t require but also doesn�t exclude the usage of threads. The stream-based library can be developed later on top of callback functions. In order to realize multithreading all library primitives should be thread safe. Primitives whose execution require a long period of time (i.e. the primitives that start event-loop or directly/indirectly execute the callback functions in Haskell) should not block the execution of the concurrent threads. The way of realizing it depends on the used compiler. For the future versions of GHC probably the best way will be by using �threadsafe� foreign calls, but for Hugs maybe the �yield� primitive. That�s why I propose not to specify the exact way of multithreading. AS for the realization, it can be done in a different way for the different combinations compiler/toolkit. Another point that I noticed is the way of registering/unregistering of the event handlers. Under Windows all events are passed to the function handler even if it ignores them. Under GTK the event is passed only if there is a registered event handler. All libraries based on X have similar design because this allows minimizing of the traffic between the application and the X server. It is a disadvantage of the GTK based Port library where all event handlers are automatically registered even if they are ignored by the Haskell program. It is not difficult to correct this, but we have to bear it in mind when making the standard. Summary: - CGA should use callback functions for event handling - All primitives should be thread safe - Calling whoever of the primitives should not block the concurrent threads. An exception can be done only for the primitives who are executed in a very short constant time - For each event there should be two functions: register and unregister I am under the impression that there is a consensus of all points mentioned above but they have to be specified in the standard. In this context I would like to ask whether there are any additions to the document that Axel wrote. Best wishes, Krasimir __________________________________________________ Do you Yahoo!? Yahoo! Web Hosting - establish your business online http://webhosting.yahoo.com

Krasimir Angelov
I am under the impression that there is a consensus of all points mentioned [below] but they have to be specified in the standard.
My impression differs on 3 of the 4 points.
Summary: - CGA should use callback functions for event handling
The concensus does seem to be that this is readily implemented and can be used to implement the other styles of interface being proposed.
- All primitives should be thread safe
There seems to be some disagreement over this and strong arguments for considering requiring single-threaded use. (There are also strong arguments for requiring thread-safety.)
- Calling whoever of the primitives should not block the concurrent threads. An exception can be done only for the primitives who are executed in a very short constant time
This decision is contingent on a multithreaded design.
- For each event there should be two functions: register and unregister
My understanding is that there should be one function 'register' which returns an 'unregister' action as part of its result. This neatly avoids the problem of needing equality tests on functions (semantically difficult) or hacks to get around this lack. -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/

OK, here's another call for votes: 1) CGA should use callbacks [I think everyone agreed] 2) 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. 3) An event loop should only dispatch one callback at once - in other words, no concurrency unless the user asks for it. 4) Thread-safety [Now that's where we didn't all agree...] 4a) All CGA functions must be called from the same concurrent Haskell thread. 4b) Same as 4a, plus: Calling CGA functions should not block other concurrent Haskell threads (except if the function is guaranteed to finish in a short time). 4c) CGA functions may be called from everywhere, at any time. Calling CGA functions should not block other concurrent Haskell threads (except if the function is guaranteed to finish in a short time). 4d) First wait and see if 4c is implementable; if not, fall back to 4b. I vote for 1,2,3, and for 4c [I'm convinced it is implementable; 4d is fine for me, too]. BTW, I think that we don't have to reach a conclusion on 4 now - as long as 3 is accepted by everyone, we can postpone any decision on 4 until we start implementing. Cheers, Wolfgang

On Mon, 17 Mar 2003 22:45:07 +0100
Wolfgang Thaller
4d) First wait and see if 4c is implementable; if not, fall back to 4b.
Well, I agree on everything and would directly vote for 4b, but instead I ask: what is the true problem here? If callbacks are serialized, what's the problem in allowing any thread to call CGA functions? There is a chance that some windowing system allows this. For example, mutual exclusion could be required for drawing on a surface, but not for registering a callback, so while a thread is drawing, another could be allowed to register callbacks. Could someone explain what are the problems? Vincenzo

Nick Name wrote:
On Mon, 17 Mar 2003 22:45:07 +0100 Wolfgang Thaller
wrote: 4d) First wait and see if 4c is implementable; if not, fall back to 4b.
Well, I agree on everything and would directly vote for 4b, but instead I ask: what is the true problem here? If callbacks are serialized, what's the problem in allowing any thread to call CGA functions?
Well, even if the callbacks are serialized, we're back to full concurrency as soon as people want to use forkIO in their applications. So if you forkIO, do some calculation, and then try to display a dialog box when the calculation is finished, you can either rely on 4c or do some interthread messaging to fulfill the requirement that everything is called from one thread. If the windowing system allows everything, we get thread-safety for free, but if it doesn't allow it, we might have to do some work to achieve it in the CGA. The amount of work required will be different for different backends. Some people have doubted that this is easy enough for us to implement, but personally I don't see why. I've added 4d to the list because I feel that it will be hard to convince them at this early stage, and we lose nothing by postponing the decision.
There is a chance that some windowing system allows this. For example, mutual exclusion could be required for drawing on a surface, but not for registering a callback, so while a thread is drawing, another could be allowed to register callbacks.
Yes. Other toolkits will just assume that there is only one thread talking to them, so everything will need mutual exclusion. Yet other toolkits will allow concurrent drawing to two different windows, but will crash when you try to open a dialog box while another thread is handling events. And so on. That's what I don't like about 4a and 4b: It will be next to impossible for application programmers to add thread-safety, because the requirements are platform dependent. Of course, 4c doesn't mean that the user will no longer have to synchronize anything - you still have to protect your application's shared state. Cheers, Wolfgang

Nick Name wrote:
4d) First wait and see if 4c is implementable; if not, fall back to 4b.
Well, I agree on everything and would directly vote for 4b, but instead I ask: what is the true problem here? If callbacks are serialized, what's the problem in allowing any thread to call CGA functions? There is a chance that some windowing system allows this. For example, mutual exclusion could be required for drawing on a surface, but not for registering a callback, so while a thread is drawing, another could be allowed to register callbacks.
Could someone explain what are the problems?
The problem is that a toolkit may not be at all re-entrant; i.e.
calling any toolkit function while another toolkit function is being
executed may have undesirable consequences.
--
Glynn Clements

Glynn Clements wrote:
The problem is that a toolkit may not be at all re-entrant; i.e. calling any toolkit function while another toolkit function is being executed may have undesirable consequences.
Exactly. In the worst case, the CGA, when invoked from another thread, would have to wait for the next timer callback to come around in order to gain access to the exclusive lock on the backend toolkit, which might have some performance implications for CGA functions executed from "background" threads (but this performance penalty is not paid for every call - after the first call, you probably get several more "for free", because the backend toolkit has not yet been re-entered by another thread). Cheers, Wolfgang

On Mon, 17 Mar 2003 23:53:38 +0100
Nick Name
I agree on everything and would directly vote for 4b
No, I made a typo: I would vote for 4d! I would like to be sure that when we will have some use for OS threads they won't destroy our previuos work. So, I re-state: I vote for 4d. Vincenzo

Wolfgang Thaller wrote:
4b) Same as 4a, plus: Calling CGA functions should not block other concurrent Haskell threads (except if the function is guaranteed to finish in a short time).
What does this entail? I.e. how do you ensure that a function doesn't
block other concurrent Haskell threads?
--
Glynn Clements

OK, here's another call for votes: My vote is 1,2,3, and 4c. I think that development of
--- Wolfgang Thaller

Hi all,
2) 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.
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 also think that "register/unregister" is a confusing name. Here is my proposal. We have two functions for each event type. One to set the current callback, and one to get the current callback. (there is only one callback at any time). buttonOnClick :: IO () -> Button -> IO () buttonGetOnClick :: Button -> IO (IO ()) windowOnMenu :: MenuItem -> IO () -> Window -> IO () windowGetOnMenu :: MenuItem -> Window -> IO (IO ()) ... 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 (ha, do we still want concurrency here :-) Or implement the yahu/gio syntax for setting/getting events: w <-window [on click := dropBall] In general, you can implement "listeners" or callback transformers like filters. Maybe one of these designs indeed returns an "unregister" function but that is up to the library writer. 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. Normally, the callbacks are deinstalled when the widget dies. But for completeness, we might want to have: buttonIgnoreOnClick :: Button -> IO () All the best, Daan.

On Tue, 18 Mar 2003 12:33:44 +0100
Daan Leijen
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.
I also think that "register/unregister" is a confusing name. Here is my proposal. We have two functions for each event type. One to set the current callback, and one to get the current callback. (there is only one callback at any time).
Yes, but: if you want to modify the current callback, for example to append another with ">>", you have to lock it if there are other haskell threads; you can't just get it, modify and put back, you need a "modifyCallback" action. This can be implemented with MVars of course. The other way around, wich is "registerCallback :: (a -> IO b) -> IO (IO ())" is more useful if one allows more than one callback at a time. It seems to me that one thing is implementable by the other, by tricking with functional equality, so I guess we will choose the one that matches more toolkits. Vincenzo

On Dienstag, Mär 18, 2003, at 12:33 Europe/Vienna, Daan Leijen wrote:
Hi all,
2) 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.
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"]
Yes, that looks nicer, but it now we can have only one callback for one event; I believe the people who first brought this design up (I was only copying what I had read on this list) were thinking of allowing multiple callbacks to be installed for a single event, as can be done using GTK's signal/slot mechanism. So what about... 2) Installing Callbacks 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. 2b) Callbacks are set using a general attribute get/set mechanism that we will probably design soon. There can be only one callback for one event. Please note that it is very easy to emulate one option using the other, so the features of the backend libraries are probably not an issue here. 2a can, of course, be useful in many situations. I'm not sure if I want the nice syntax or the nice functionality, so I withdraw my vote on 2) and abstain for now. Everyone please keep discussing :-) I'd like to provide an example of where I'd prefer to have 2a: Suppose we have a document window. In order to support a "Save Changes?" dialog, I'd perhaps add a callback for the "window about to close" event. No problems here. Suppose that we also have a utility window that shows information about some item in the document. When the document window closes, the utility window closes, too (we'd like to have a second "close" callback on the document window), but the utility window can also be closed by the user before the document closes (we'd like to uninstall our "close" callback in that case). In most other situations, I would probably prefer the single-callback approach because of the opportunities for nicer syntax.
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
Which is fine as long as you don't want to uninstall a callback. And we might want to use a threadsafe buttonModifyOnClick :: Button -> (IO () -> IO ()) -> IO () instead.
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. Normally, the callbacks are deinstalled when the widget dies. But for completeness, we might want to have:
buttonIgnoreOnClick :: Button -> IO ()
Definitely. For button clicks, it is a non-issue, but some X folk have said that mouse moved events are really bad for remote connections, and I wouldn't bet it's so unusual to track mouse movements only for a short period of time and then uninstall the callback. Cheers, Wolfgang

On Tue, Mar 18, 2003 at 12:33:44PM +0100, Daan Leijen wrote:
Hi all,
2) 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.
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 ())
My proposal: 2c) Unregistering callbacks is useful (perhaps mandatory on X) but not used very often. Thus most callbacks can be bound by button [on click := messageBox "hi there"] without any drawback, so let's keep this syntax! In case we need an unregister function we can use something like button [onOff click remove := messageBox "hi there"] where remove has type MVar (IO ()). This might seem clumsy at first, but note that you usually need to store this unregister function in your program state anyway, so that you might be able to pass a piece of your local state in. Furthermore if the MVar is full, the IO action could just be added to the action in the MVar. Opinions? Axel.

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

On Tue, 18 Mar 2003 18:46:42 +0000
Glynn Clements
On X, it's important to deregister callbacks
On opengl, too, the mouse motion callback is heavy, in case that X alone doesn't suffice :) V. -- Teatri vuoti e inutili potrebbero affollarsi se tu ti proponessi di recitare te [CCCP]

On Tue, 18 Mar 2003 18:46:42 +0000
Glynn Clements
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.
I am perhaps missing the point. In my view, if you want to simulate multiple callbacks with just one, you can create the FunPtr in the callback registration, and keep it into a closure you give back to unregister the callback. Of course, the multiple callback is just an interface, and toolkits wich support multiple callbacks natively will not go trough this mess. What do toolkits think about this? :) What toolkits implement one callback, and what multiple ones? -- Scopriti essere umano e in quanto tale persona banale e non speciale a cui Dio concede gesti assai banali. D'ora in poi quello sei tu. [Marlene Kuntz]

On Tue, 18 Mar 2003 20:51:16 +0100
Nick Name
you can create the FunPtr
or else, of course, to be portable, you can assign an unique integer to any registered callback, wich is then used to unregister it. This is what's already there in Gtk2. V. -- First they ignore you, then they laugh at you, then they fight you, then you win. [Gandhi]

Nick Name wrote:
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.
I am perhaps missing the point.
The point is that if: a) you want multiple callback procedures for a single event, and b) you want to be able to de-register individual callbacks, then there has to be some way of specifying *which* callback procedure you wish to de-register. Given that functions (or plain IO actions, in the case where the callback doesn't take parameters) don't implement Eq, you can't just pass the expression which was passed to the registration function. It doesn't matter whether the underlying toolkit supports multiple callbacks directly or whether you emulate them; the problem is in deciding upon the Haskell interface.
you can create the FunPtr
or else, of course, to be portable, you can assign an unique integer to any registered callback, wich is then used to unregister it.
AFAICT, if you're going to have the registration function return some
kind of entity to enable a specific procedure to be de-registered, you
may as well just return a "canned" IO action.
--
Glynn Clements

On Tue, 18 Mar 2003 23:44:51 +0000
Glynn Clements
The point is that if:
a) you want multiple callback procedures for a single event, and b) you want to be able to de-register individual callbacks,
then there has to be some way of specifying *which* callback procedure you wish to de-register
Yes, this is clear; it's easy to get misunderstood on the web. I was just saying to Daan, in the post you answered, that having multiple callbacks, and so having to return something in the act of registration, does not mean that one can't use nicer syntaxes (syntaxen?). If you want button [onClick =: doSomething] you can have it, you will find your result, the unregister action, among other fields in the result type. If you don't want to put it into the "Button" datastructure, things get really complicated, yes it's so. Notice that you are not requiring the user to pass a FunPtr to the registration action anywhere, nor using it in the CGA, if the toolkit provides multiple callbacks. BTW, this behaviour would feel somewhat right to me: if you have a "preferred" callback (or you are working with just one), this is the one you'll find in the button's "unregister callback" field. Vincenzo -- Teatri vuoti e inutili potrebbero affollarsi se tu ti proponessi di recitare te [CCCP]

Axel Simon wrote:
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. Gtk uses a very similar approach in that each event can have several callbacks registered and you can remove each callback individually. So I
On Tue, Mar 18, 2003 at 06:46:42PM +0000, Glynn Clements wrote: think my proposal serves this 1:n correspondance. You can say: remove :: MVar (IO ()) <- newEmptyMVar button [label =: "Open Dialogs", on click := messageBox "hi there", onOff click remove := messageBox "bye there"] which will attach two callbacks to the same event whereas the remove MVar will only get the unregister function for the second callback. If you want to remove both at a time you say: button [label =: "Open Dialogs", opeonOff click remove =: messageBox "hi there", onOff click remove =: messageBox "bye there"] and "withMVar remove id" as an IO action will remove both callbacks. Note: a) fits into syntax b) 1:n event:callback correspondance c) the MVar approach is actually convenient in practice (as you need to store the unregister function/signal-id somewhere anyway) d) can be implemented with equality on C function pointers under the hood if necessary No? Axel.

On Wed, 19 Mar 2003 14:46:47 +0000
Axel Simon
Gtk uses a very similar approach in that each event can have several callbacks registered and you can remove each callback individually. So I think my proposal serves this 1:n correspondance
I can think of many other ways to solve the problem. And, besides, I can see many other ways to initialize, and access fields of, widgets and things that have properties, in general. Now I play the role you played against me last week :) : if all the toolkits allow it, can we have function wich take no initializers, and provide get and set functions for attributes? Other approaches are implementable on top of this really weird interface. This is an attempt to lower the priority of the problem of handling local state of the widgets. When we have a strong basis well built, and working flawlessly on various platforms, we can create teams to standardize the mid-level syntax. Now, I know that I was saying the opposite last week; you really convinced me that it's best to solve low-level issues before mid-level ones, and I wish people on the list to have time to elaborate what the various alternatives can be. Besides, I see a very clear distinction between what's low-level and what's mid-level: - low level is everything that needs FFI and/or writing to handles to be implemented. - mid level is everything that can be implemented in haskell, over the low level ground. So, I ask to everybody: would it be right to provide a working low-level interface, in the IO monad, even if it sucks completely, call it "the CGA core", and ONLY THEN think about issues as initialization syntax and a nice way to handle local state? Note that this does not mean that we'll claim that our work is finished, when we have the core. Instead, we'll really focus on the mid-level, and produce a good one, before declaring the CGA as a standard; this is necessary IMHO. Let's vote for this, here are the two alternatives 1. to have at least a simple syntax wich is not like an assembly language is good, and comfortable even when writing the low-level stuff; so it's better to design some standard mid-level interface right now 2. by designing a weird interface in the IO monad, similar to the ones we find in traditional imperative languages, we will produce the core much faster; it will be minimal and open to other implementation; also, people will have more time to think to nice syntaxen and the state handling issues; so it's better not to do any mid-level stuff right now. I obviously vote for 2, but I prefer to count much less than other, more experienced people on this list. Vincenzo

Vincenzo Ciancia wrote:
2. by designing a weird interface in the IO monad, similar to the ones we find in traditional imperative languages, we will produce the core much faster; it will be minimal and open to other implementation; also, people will have more time to think to nice syntaxen and the state handling issues; so it's better not to do any mid-level stuff right now.
A related issue is that, ideally, the common API should start at as
low a level as possible, so that as much code as possible only needs
to be implemented once rather than separately for each back-end.
The inherent differences between the various back-ends will inevitably
provide a push towards a higher-level interface; it's up to the
designers to provide the opposing force.
--
Glynn Clements

Sorry for the long mail, I had no time for a shorter one. (Goethe) On Thu, Mar 20, 2003 at 01:29:26AM +0100, Vincenzo Ciancia wrote:
Axel Simon
wrote: Gtk uses a very similar approach in that each event can have several callbacks registered and you can remove each callback individually. So I think my proposal serves this 1:n correspondance
I can think of many other ways to solve the problem. And, besides, I can see many other ways to initialize, and access fields of, widgets and things that have properties, in general. Let's hear it :-). The MVar approach has surely disadvantages: In the low-level gtk2hs veneer you can say right now:
sid <- button `onClicked` putStrLn "Hello" button2 `onClicked` disconnect sid This code attaches a callback to the first button and disconnects it when the second button is pressed (probably a bonkers example). With the MVar approach we would need to write something like: remove <- newEmptyMVar button <- newButton [onOff click remove := putStrLn "Hello"] button2 <- newButton [on click := withMVar remove id] This is surely a little clumsy. If the majority of removal functions for callbacks are done like this, then my approach is not really nice. If removal functions are usually stored in an MVar in the program state, then I think its ok.
Now I play the role you played against me last week :) : if all the toolkits allow it, can we have function wich take no initializers, and provide get and set functions for attributes? Other approaches are implementable on top of this really weird interface. Ok, let me try to recover my argument: Having a mid-level API should enable the user to write applications concisely. Thus if a) passing a list of attributes to the constructor makes code shorter and b) we can agree on a design where advantages are clear and dominate, we should have it in CGA.
My impression was that the event/callback issue was a little religous. As such, the advantages where not too clear for everybody. Maybe the same thing holds for the setting attribute issue. [..]
Besides, I see a very clear distinction between what's low-level and what's mid-level:
- low level is everything that needs FFI and/or writing to handles to be implemented.
- mid level is everything that can be implemented in haskell, over the low level ground.
So, I ask to everybody: would it be right to provide a working low-level interface, in the IO monad, even if it sucks completely, call it "the CGA core", and ONLY THEN think about issues as initialization syntax and a nice way to handle local state?
Note that this does not mean that we'll claim that our work is finished, when we have the core. Instead, we'll really focus on the mid-level, and produce a good one, before declaring the CGA as a standard; this is necessary IMHO.
Clarification: What I meant by the "core" so far was the functionlity to set/get attributes, how to organize widgets with type classes, how to model callbacks/events, etc. To implement these issues we probably have to talk about what low-level functionality we have in our backends and how we would like the mid-level CGA to look like. For all other stuff (set of widgets, their functionality, application setup (MDI, SDI,..), window types,...) we can probably get away by just defining the mid-level functions we think are sensible without explicitly stating how the underlying IO actions are called. I.E. we only need to say we want createButton :: [ButtonAttribute] -> IO Button with the attributes clicked, label, ... but not that this function calls these function buttonNew :: IO Button buttonSetLabel :: Button -> String -> IO () onClicked :: ButtonClass b => b -> IO () -> IO (ConnectId b) since these are up to each backend implementation.
Let's vote for this, here are the two alternatives
1. to have at least a simple syntax wich is not like an assembly language is good, and comfortable even when writing the low-level stuff; so it's better to design some standard mid-level interface right now
2. by designing a weird interface in the IO monad, similar to the ones we find in traditional imperative languages, we will produce the core much faster; it will be minimal and open to other implementation; also, people will have more time to think to nice syntaxen and the state handling issues; so it's better not to do any mid-level stuff right now.
I hope to define the "core" as it appears in the poll document with 1) supported by arguments on the level of 2). The rest of CGA will hopefully be defined in terms of 1). If events and the synchronization points (the two issues again) a) make programming CGA easier and conciser and b) we all agree on an implementation then it should be in CGA. Everytime we don't agree on b), I try to mediate by asking if we could implement the issue on top of other functionality. Axel.

On Thu, 20 Mar 2003 10:43:47 +0000
Axel Simon
Ok, let me try to recover my argument: Having a mid-level API should enable the user to write applications concisely. Thus if a) passing a list of attributes to the constructor makes code shorter and b) we can agree on a design where advantages are clear and dominate, we should have it in CGA.
We will have it in CGA, be sure that I won't surrender until we have a very good mid-level approach. Just what I want to say is: let's do a first level of implementation, wich is the one bound to FFI, and care of the syntax, and the state handling, only later, when we will have some working widget. I absolutely don't want the "standard haskell GUI" to look like an imperative library, or an imperative subset to be declared "complete" with everybody abandoning the project. We will surely use haskell great expressive power. Just, because choosing the right syntax (and if to use or not to use type classes, etc) requires much more work than just building the fundamentals, I propose to have a milestone, which is having a pure "assembly-like" (if you pass the term) implementation. Then, we'll focus on the mid-level, wich, I repeat, *has* to be done (in my opinion) before declaring the CGA in an alpha state. I need this incremental approach to have time to test various ways to handle state and initialization, maybe others have clearer ideas and want to work on that immediately, so I call for votes. Vincenzo

Hello, I've got two Ideas: 1) Add a haskell extention (wouldn't this be possible?) that allows for equality for functions. Maybe only valid for IO() functions. Say we have: a :: IO () We know that (a == a) but a doesn't equal anything else. This would make the unregister process much neater. 2) Would it not be possible to do callbacks with lists? button [ onClick := [messageBox "hi there"] ] add a callback: set myButton [ onClick :~ (/a -> newFunction:a) ] And if the onClick function found it had an empty list, it would unregister the callback with the underlying library. David J. Sankel

David Sankel wrote: [I don't know whether 1) is practical, but I suspect not.]
2) Would it not be possible to do callbacks with lists?
button [ onClick := [messageBox "hi there"] ]
add a callback:
set myButton [ onClick :~ (/a -> newFunction:a) ]
And if the onClick function found it had an empty list, it would unregister the callback with the underlying library.
It may be possible, but it's unlikely to be a good idea.
IMHO, code should only de-register those callbacks which it knows
about. Removing all callbacks, even those which may have been
installed for unknown reasons by unknown code (e.g. the toolkit
itself) is bound to be wrong.
Certainly, Xt allows this (XtRemoveAllCallbacks()); it doesn't,
however, make any guarantees regarding the consequences.
--
Glynn Clements

1) Add a haskell extention (wouldn't this be possible?) that allows for equality for functions. Maybe only valid for IO() functions. Say we have:
a :: IO ()
We know that (a == a) but a doesn't equal anything else. This would make the unregister process much neater.
In general, we can only do equality on objects which are either: a) Pure data Ints, Floats, tuples and arrays of pure data, etc. - not functions. b) Constructed in the IO monad For example, we can do equality checks on IORefs, MVars, etc. because the only way to construct these objects is by invoking an operation of type '... -> IO ...'. The reason being in the IO monad is important is that it gives us a handle on sharing: each invocation of an IO operation is allowed to return a distinct result whereas separate invocations of non-IO operations are required to give the same result. At first glance, it seems easy enough to do equality on things in the IO monad since it seems we could treat >>= and return as constructors. The problem is that any useful IO code tends to involve conditionals, pure expressions, etc. for which equality isn't feasible/ meaningful. -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/

4c) CGA functions may be called from everywhere, at any time. Calling CGA functions should not block other concurrent Haskell threads (except if the function is guaranteed to finish in a short time).
4d) First wait and see if 4c is implementable; if not, fall back to 4b. I vote for 4c but I have a question which probably belongs to the FFI
Cross-posting this to FFI@haskell.org. On Mon, Mar 17, 2003 at 10:45:07PM +0100, Wolfgang Thaller wrote: list. Maybe I am just not understanding the intentions of the FFI but I believe that I cannot make Gtk calls threadsafe from within Haskell. Gtk's thread issues are shortly explained at: http://developer.gnome.org/doc/API/2.0/gdk/gdk-Threads.html The issue seems to be that every call to Gtk has to be surrounded by a call to gdk_threads_enter and gdk_threads_leave. How am I supposed to bind these functions if I don't have any control about which thread executes a call? Suppose a) I do foreign import ccall unsafe "gdk_threads_enter" enter :: IO () foreign import ccall threadsafe "gtk_function" function :: IO () foreign import ccall unsafe "gdk_threads_leave" leave :: IO () and execute "enter >> function >> leave" is giving me unique access to the Gtk toolkit if every Gtk function call is surrounded by the "enter" and "leave" statements. Disadvantage: calling "enter" will block the whole Haskell system if there is another (OS) thread executing in Gtk. b) I change foreign import ccall unsafe "gdk_threads_enter" enter :: IO () to foreign import ccall threadsafe "gdk_threads_enter" enter :: IO () and loose safety: All "enter" calls hijack an OS thread and wait for the lock to be available. Meanwhile in Haskell, the Gtk function is called. It seems that it is necessary to state that these three functions all execute in the same thread. I don't want to write C wrappers for all Gtk functions! I hope I am missing something here. Axel.

It seems that it is necessary to state that these three functions all execute in the same thread. I don't want to write C wrappers for all Gtk functions!
Yes, that's what the current "bound threads" proposal is all about, to allow a Haskell thread to be "bound" to a specific OS thread so that all three functions are guaranteed to execute in the same thread. I think that GHC probably won't ship with the "threaded RTS" enabled before this or an equivalent proposal has been accepted and implemented. Without the threaded RTS (e.g. GHC 5.04), OS threads are a non-issue, as all Haskell code runs in one OS thread. So no need to worry there. Cheers, Wolfgang

On Sun, Mar 16, 2003 at 12:10:03PM -0800, Krasimir Angelov wrote:
In this context I would like to ask whether there are any additions to the document that Axel wrote. I started to incooperate text to the document, but I am still stuck in February. I hope to catch up and put it into the GHC CVS. (Maybe I do the latter first, so other people can have a go.)
Axel.
participants (9)
-
Alastair Reid
-
Axel Simon
-
Daan Leijen
-
David Sankel
-
Glynn Clements
-
Krasimir Angelov
-
Nick Name
-
Vincenzo Ciancia
-
Wolfgang Thaller