From: Anthony Tavener <anthony.tavener@gmail.com>
To: Philippe Veber <philippe.veber@gmail.com>
Cc: caml users <caml-list@inria.fr>
Subject: Re: [Caml-list] Functional GUI programming: looking for good practices
Date: Tue, 14 Feb 2012 21:47:10 -0700 [thread overview]
Message-ID: <CAN=ouMQQYAKcQHYV_A6QuN0-J=KMPFtJ3zWVr9BFQOrpZ4v1Pw@mail.gmail.com> (raw)
In-Reply-To: <CAN=ouMSeauccf_+3upwsmTCxNzyKJBMkvjmatbKaV=4j=AscmQ@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 6509 bytes --]
Hrm... when I re-read my prior post before sending, it made sense. Several
hours later it seems inadequate, and I've thought of something to say more
clearly...
The execution of code bounces between the UI and the mainline. When the
mainline processes a "gui hit" it will resume the UI code *right where it
left off (yielded)*... then the UI will do something and get to another
point where it yields, awaiting input and thereby resuming the mainline
where it was (back to processing gui hits).
Why would I want this? So that I don't have stateful UI code which has to
trickle down to the right place all the time. The UI code is clearer
because it doesn't need to be re-entered from the top each frame. This
suits the declarative style of GUI specification, like the example you
gave, since we don't need special state or messages to communicate what the
GUI is doing. GUI state becomes a property of the... well, the stack. It's
the current scope of the code.
Hope this helps!
-Tony
On Tue, Feb 14, 2012 at 11:02 AM, Anthony Tavener <anthony.tavener@gmail.com
> wrote:
> 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 <philippe.veber@gmail.com>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
>>>
>>>
>>
>
[-- Attachment #2: Type: text/html, Size: 9057 bytes --]
next prev parent reply other threads:[~2012-02-15 4:47 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-02-13 11:01 Philippe Veber
2012-02-13 15:27 ` Adrien
2012-02-14 10:02 ` Philippe Veber
2012-02-14 10:21 ` Daniel Bünzli
2012-02-14 10:39 ` Philippe Veber
2012-02-14 11:52 ` Adrien
2012-02-14 13:00 ` Daniel Bünzli
2012-02-14 13:29 ` Adrien
2012-02-13 18:13 ` Raoul Duke
2012-02-14 0:36 ` Anthony Tavener
2012-02-14 10:17 ` Philippe Veber
2012-02-14 18:02 ` Anthony Tavener
2012-02-15 4:47 ` Anthony Tavener [this message]
2012-02-22 11:57 ` Philippe Veber
2012-02-28 10:10 ` Adrien
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='CAN=ouMQQYAKcQHYV_A6QuN0-J=KMPFtJ3zWVr9BFQOrpZ4v1Pw@mail.gmail.com' \
--to=anthony.tavener@gmail.com \
--cc=caml-list@inria.fr \
--cc=philippe.veber@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox