From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail4-relais-sop.national.inria.fr (mail4-relais-sop.national.inria.fr [192.134.164.105]) by walapai.inria.fr (8.13.6/8.13.6) with ESMTP id q1EI2CKx005577 for ; Tue, 14 Feb 2012 19:02:16 +0100 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AngBAHygOk9KfVI0imdsb2JhbABDp3cBiCcIIgEBAQoJDQcSBiOBcgEBAQMBEgITGQEbHQEDAQsGBQs7IQEBEQEFARwGEyKHWgmfTwqLcYJwhQ4/gQsCBQuIOYMTAwQICQEJBw8FhQuDSQSISoxoixCDFT2EIg X-IronPort-AV: E=Sophos;i="4.73,417,1325458800"; d="scan'208";a="131340950" Received: from mail-ww0-f52.google.com ([74.125.82.52]) by mail4-smtp-sop.national.inria.fr with ESMTP/TLS/RC4-SHA; 14 Feb 2012 19:02:15 +0100 Received: by wgbds10 with SMTP id ds10so232421wgb.9 for ; Tue, 14 Feb 2012 10:02:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=7/imjnnOzeN6etC/J/nclQt00UydGO5KXiEmvvOh/KI=; b=pvreMbDMq7g9tta2vnrB9dTCsC1MvBXdrNNQP5A+2zpVqNJs6CK9BFQFQdsw9vqP6r xV+pY84+btFrjWSHedlEOyO7tcwajRBQ1xlYmrhBf9z8eYL4E/GrOgGPtJH81xI3CU8e D9xHw11VKWvA0w1C1krUYtbGg7XsDWwhWZC+4= MIME-Version: 1.0 Received: by 10.216.139.197 with SMTP id c47mr1343684wej.15.1329242535345; Tue, 14 Feb 2012 10:02:15 -0800 (PST) Received: by 10.223.7.69 with HTTP; Tue, 14 Feb 2012 10:02:15 -0800 (PST) In-Reply-To: References: Date: Tue, 14 Feb 2012 11:02:15 -0700 Message-ID: From: Anthony Tavener To: Philippe Veber Cc: caml users Content-Type: multipart/alternative; boundary=0016e6de00b123bc8c04b8f0657b Subject: Re: [Caml-list] Functional GUI programming: looking for good practices --0016e6de00b123bc8c04b8f0657b Content-Type: text/plain; charset=ISO-8859-1 Apologies Philippe, this is a bit long... The "update loop" I mentioned might be a bit of a red-herring, as I'm only using that for continuously active UI elements: such as changing cursor to represent the action which would be taken on click. It has nothing to do with the basic UI flow. I didn't understand delimcc right away, and I hardly understand it now! :) I was looking to write UI code much as your example of packing buttons together with directly bound activation functions. Here's my "menu_of_list", which takes a list of string * 'a pairs, calling GUI code to make a menu using the strings as labels, and using the 'a values as return values... let menu_of_list lst return = (* snipped: preamble which handles positioning, stacking order, getting font, etc *) Gui.add_widget gui (new Gui.vbox pos stacking spacing (lst |> List.map (fun (label,value) -> Gui.wit_fn gui (new Gui.rectangle_text w h fnt label) (fun () -> return (Some value) ) ))) The important part here is the "return" function. This will resume the UI coroutine. It is given to "menu_of_list" by the UI code, then when the GUI has an activation of a menu button this "return" is called... resuming the UI where it left off, and with the "Some value" which was associated to the button. The magic of how this works is in delimcc capturing portions of the run stack. Here I've extracted the relevant bits of code: (* this runs as a coroutine with the "mainline" *) let ui ui_process () = (* in actual code, this menu comes after a "right click", for special actions *) let act = yield ui_process (menu_of_list coord [ ("Equip",`Equip); ("Spell",`Spell); ("End",`End) ] ) in (* ... handle whatever action the user chose! *) (* given SDL events, calls activation functions of 'hit' widgets *) let gui_react gui events = let hits = gui_select gui events in match hits with | h::t -> begin match get_binding gui h with | Some f -> f () (* runs activation function of widget, such as "return" to coroutine! *) | None -> () end | [] -> () let _ = (* A prompt is delimcc's mechanism for marking the stack to -limit- the * continuation, rather than creating whole-program continuations. *) (* So here, the "ui" is initially run, and will *resume* this mainline continuation * at the end of the "user" function call. *) let ui_prompt = new_prompt () in push_prompt ui_prompt (ui ui_prompt); ... (* in mainloop *) gui_react gui events; ---------------------- Now I'm going to restate the yield function here, and try to explain... let yield level fn = shift level (fun k -> fn k; abort level () ) "shift" and "abort" are delimcc. This runs the given function "fn" with a continuation 'k'... so this continuation is the "return" function passed to menu_of_list, and therefore bound to each menu-button. The abort exits the "ui coroutine", resuming the mainline, hence the name: yield. The continuation 'k' comes from the shift... the continuation is the code "outside" of the shift call... so when it's called (by the mainline's gui_react), it resume at the end of 'yield' and keep on going... in this example, handling the returned action! I hope this conveys at least the gist of what's going on... I read a lot of papers over-and-over, not understanding... although none were specifically GUI. Delimited continuations have numerous applications and a surprising number of configurations for just a pair of functions! (Abort is really a special case of "reset"... so it's shift and reset, in ump-teen configurations.) I'll try to explain if you have any further questions! However, I'm still trying to sort out how best to write my GUI code -- there is a lot of room for improvement. :) -Tony PS. I'd like to blame X. Leroy ;)... for a post some 10 years ago replying to someone's attempt to do a GUI in OCaml... Xavier casually replied something like "oh, you can use coroutines". That was a wild goose chase (or Dahu?)! Delimcc didn't exist at the time, and it's native-code implementation came about just a few years ago (thank-you Oleg!). Even then, I had no idea how I was supposed to use a coroutine to write GUI code! Oh well, it was an adventure, and I still don't know if this is a good thing, but I like it a lot more than "signals and slots" -- the usual scattered GUI code which is connected by messages/events. On Tue, Feb 14, 2012 at 3:17 AM, Philippe Veber wrote: > Hi Anthony, > > This looks interesting, however as I'm not very familiar with delimcc > (that's a shame, I admit), I fail to understand the flow of the program. > Would you mind giving a snippet of the update loop you mentionned? > > >> So far, I'm not sure how well this works out for a complete project. I >> like it so far, but I have complexity growing in some "update loop" stuff, >> which are little closures I add to be run each frame-update for reacting to >> mouse-over/hover. >> >> > > Good luck in the hunt for elegant UI (code)! >> > > Let's hope I'm not just Dahu hunting! > (http://en.wikipedia.org/wiki/Dahu) > > > >> >> Tony >> >> > --0016e6de00b123bc8c04b8f0657b Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Apologies Philippe, this is a bit long...

The "updat= e loop" I mentioned might be a bit of a red-herring, as I'm only u= sing that for continuously active UI elements: such as changing cursor to r= epresent the action which would be taken on click. It has nothing to do wit= h the basic UI flow.

I didn't understand delimcc right away, and I hardl= y understand it now! :)

I was looking to write UI = code much as your example of packing buttons together with directly bound a= ctivation functions.

Here's my "menu_of_list", which takes a l= ist of string * 'a pairs, calling GUI code to make a menu using the str= ings as labels, and using the 'a values as return values...

let menu_of_list lst return =3D
=A0 (* snipped: pr= eamble which handles positioning, stacking order, getting font, etc *)
=A0 Gui.add_widget gui
=A0 =A0 (new Gui.vbox pos stacking s= pacing
=A0 =A0 =A0 (lst |> List.map
=A0 =A0 =A0 =A0 (fun (label,= value) ->
=A0 =A0 =A0 =A0 =A0 Gui.wit_fn gui
=A0 =A0= =A0 =A0 =A0 =A0 (new Gui.rectangle_text w h fnt label)
=A0 =A0 = =A0 =A0 =A0 =A0 (fun () -> return (Some value) ) )))

The important part here is the "return" funct= ion. This will resume the UI coroutine. It is given to "menu_of_list&q= uot; by the UI code, then when the GUI has an activation of a menu button t= his "return" is called... resuming the UI where it left off, and = with the "Some value" which was associated to the button.

The magic of how this works is in delimcc capturing por= tions of the run stack. Here I've extracted the relevant bits of code:<= /div>


(* this runs as a coroutine with th= e "mainline" *)
let ui ui_process () =3D

=A0 (* in actual cod= e, this menu comes after a "right click", for special actions *)<= /div>
=A0 let act =3D yield ui_process (menu_of_list coord
= =A0 =A0 [ ("Equip",`Equip);
=A0 =A0 =A0 ("Spell",`Spell);
=A0 =A0 =A0 ("E= nd",`End) ] ) in
=A0 (* ... handle whatever action the user = chose! *)


(* given SDL events, call= s activation functions of 'hit' widgets *)
let gui_react gui events =3D
=A0 let hits =3D gui_select gui= events in
=A0 match hits with
=A0 | h::t -> begin
=A0 =A0 match get_binding gui h with
=A0 =A0 | Some f -&= gt; f () =A0(* runs activation function of widget, such as "return&quo= t; to coroutine! *)
=A0 =A0 | None -> ()
=A0 =A0 end
=A0 | [] ->= ()

let _ =3D
=A0 (* A prompt is delimcc= 's mechanism for marking the stack to -limit- the
=A0 =A0* co= ntinuation, rather than creating whole-program continuations. *)
=A0 (* So here, the "ui" is initially run, and will *resume*= this mainline continuation
=A0 =A0* at the end of the "user= " function call. *)
=A0 let ui_prompt =3D new_prompt () in
=A0 push_prompt ui_prompt (ui ui_prompt);

=A0 ...<= /div>

=A0 (* in mainloop *)
=A0 =A0 gui_react = gui events;
=A0=A0
----------------------
Now I'm going to restate the yield function here, and try to explain...=

let yield level fn =3D
=A0 shift level = (fun k ->
=A0 =A0 fn k;
=A0 =A0 abort level () )

"shift" and "abort" are delimcc. This runs t= he given function "fn" with a continuation 'k'... so this= continuation is the "return" function passed to menu_of_list, an= d therefore bound to each menu-button. The abort exits the "ui corouti= ne", resuming the mainline, hence the name: yield.

The continuation 'k' comes from the shift... th= e continuation is the code "outside" of the shift call... so when= it's called (by the mainline's gui_react), it resume at the end of= 'yield' and keep on going... in this example, handling the returne= d action!

I hope this conveys at least the gist of what's goi= ng on... I read a lot of papers over-and-over, not understanding... althoug= h none were specifically GUI. Delimited continuations have numerous applica= tions and a surprising number of configurations for just a pair of function= s! (Abort is really a special case of "reset"... so it's shif= t and reset, in ump-teen configurations.)

I'll try to explain if you have any further questio= ns! However, I'm still trying to sort out how best to write my GUI code= -- there is a lot of room for improvement. :)

=A0-Tony

PS. I'd like to blame X. Leroy ;)... = for a post some 10 years ago replying to someone's attempt to do a GUI = in OCaml... Xavier casually replied something like "oh, you can use co= routines". That was a wild goose chase (or Dahu?)! Delimcc didn't = exist at the time, and it's native-code implementation came about just = a few years ago (thank-you Oleg!). Even then, I had no idea how I was suppo= sed to use a coroutine to write GUI code! Oh well, it was an adventure, and= I still don't know if this is a good thing, but I like it a lot more t= han "signals and slots" -- the usual scattered GUI code which is = connected by messages/events.


On Tue, Feb 14, 20= 12 at 3:17 AM, Philippe Veber <philippe.veber@gmail.com> wrote:
Hi Anthony,

This looks interesting, however as I'm not very fami= liar with delimcc (that's a shame, I admit), I fail to understand the f= low of the program. Would you mind giving a snippet of the update loop you = mentionned?
=A0
So far, I'm not sure how we= ll this works out for a complete project. I like it so far, but I have comp= lexity growing in some "update loop" stuff, which are little clos= ures I add to be run each frame-update for reacting to mouse-over/hover.



Go= od luck in the hunt for elegant UI (code)!

Let's hope I'm not just Dahu hunt= ing!
(h= ttp://en.wikipedia.org/wiki/Dahu)

=A0

=A0Tony



--0016e6de00b123bc8c04b8f0657b--