From: Philippe Veber <philippe.veber@gmail.com>
To: Anthony Tavener <anthony.tavener@gmail.com>,
Adrien <camaradetux@gmail.com>
Cc: caml users <caml-list@inria.fr>
Subject: Re: [Caml-list] Functional GUI programming: looking for good practices
Date: Wed, 22 Feb 2012 12:57:44 +0100 [thread overview]
Message-ID: <CAOOOohR-zvcvHazu0JjXx2ZQ+FdNoPUQEEpNAk7em+76rU1rbg@mail.gmail.com> (raw)
In-Reply-To: <CAN=ouMQQYAKcQHYV_A6QuN0-J=KMPFtJ3zWVr9BFQOrpZ4v1Pw@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 6891 bytes --]
Thank you very much Anthony and Adrien, your comments helped me a lot, I
now have plenty of material to think on!
Best,
ph.
2012/2/15 Anthony Tavener <anthony.tavener@gmail.com>
> 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: 9624 bytes --]
next prev parent reply other threads:[~2012-02-22 11:58 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
2012-02-22 11:57 ` Philippe Veber [this message]
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=CAOOOohR-zvcvHazu0JjXx2ZQ+FdNoPUQEEpNAk7em+76rU1rbg@mail.gmail.com \
--to=philippe.veber@gmail.com \
--cc=anthony.tavener@gmail.com \
--cc=camaradetux@gmail.com \
--cc=caml-list@inria.fr \
/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