From: Maurizio Colucci To: lablgtk at kaba.or.jp Subject: Getting rid of the imperative style Date: Tue, 17 Aug 2004 01:23:20 +0200 MIME-Version: 1.0 Content-Disposition: inline Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Message-Id: <200408170123.20592.seguso.forever at tin.it> Hello, Currently lablgtk (and in general any library based on callbacks) forces an imperative style on the program. In other words, when using lablgtk you cannot avoid using the := operator. The problem is that the type of callbacks cannot be changed. For example, suppose we have a type type mouse = {x: float; y:float} Ideally, my mouse-click callback should output a "mouse" record, that is then read by the main program logic, but I can't do that because the type of the mouse callback function is immutable. The only thing I can do is to create a global variable and have the callback write that: let global_mouse = ref {x:0; y:0} let main () = let mouse_motion_callback ev = global_mouse := {x = GdkEvent.Motion.x ev; y = GdkEvent.Motion.y ev} true in let ar = GlGtk.area [`USE_GL ; `RGBA; `DOUBLEBUFFER] ~packing:window#add ~show:true () in let _ = area#event#connect#motion_notify ~callback: mouse_motion_callback in GMain.Main.main () as you can see, the callback must use := and write on a global location, thus giving up the functional paradigm. On the contrary, it would be nice to be able to use a functional style. Fortunately, there is a way to rewrite that code without :=. The rewritten code should have an explicit main loop, that is, we don't call Gmain.Main.main anymore. Furthermore, lablgtk should supply a primitive function pending_events: () -> event list Using this primitive (whose meaning will be clear in a while), the code would become: let rec main_loop mouse = let evs = pending_events () in (* destructive read! *) let (mouse', quit) = manage_events evs mouse in if quit then () else main_loop mouse' let rec manage_events ev mouse = match ev with [] -> (mouse, false) | e::t -> let (mouse', quit) = manage_one_event e mouse in manage_events t mouse' let manage_one_event ev mouse = match ev with Mouse_moved (x2,y2) -> ({x= x2; y= y2}, false) | WidgetDestroyed w -> (mouse, true) | ... | _ -> (mouse, true) let main () = let ar = GlGtk.area [`USE_GL ; `RGBA; `DOUBLEBUFFER] ~packing:window#add ~show:true () in let _ = area#event#connect#motion_notify (* register for the Mouse_moved event *) main_loop initial_mouse Where callbacks don't exist anymore, and every function is passed all it needs. --------- Under the hood, labelgtk should do something like type event = Mouse_moved of int * int | Mouse_pressed of int | RedrawScreen | widgetDestroyed of widget | ... let pending_events_global = ref [] let pending_events () = let exit = Glib.Main.iteration true (* calls one or more callbacks and fills pending_events_global *) in let p = !pending_events_global in pending_events_global := []; p And define as many callbacks as there are events. Each of the callbacks should simply append an event at the end of pending_events_global. --------- Comments are welcome :-) Maurizio