Functional Reactive GUI for O'Caml

Hi all,

Since functional reactive programming has come up here a few times in the past, I figured my posting of this wouldn't be too out-of-place...

I've been working for the past few months on a functional reactive GUI system for O'Caml called "O'Caml Reactive Toolkit". The core FR logic is based heavily on Haskell's Yampa, while the FR API is modelled after PLT Scheme's FrTime (users of FrTime should feel at home with the API), so there is not much new there. The novelty is in the GUI API, which constructs a GUI via functions, in contrast to systems such as Fudgets (which uses arrows), or SuperGlue (which uses objects with explicit signal connections). The goal in API design is simplicity and clarity. As an example, here is a toy temperature conversion program:

open Fr
open FrGui

let _ =
    let temp_box, temp = float_entry 20. in
    let fahrenheit = lift ((@temp -. 32.) *. 1.8)
    and celsuis = lift (@temp /. 1.8 +. 32.) in
    let main_window = window
        ~title: "Temperature converter"
        (hbox [
            hbox [label "Convert "; temp_box; label " to:"];
            vbox [
                hbox [label "degrees Fahrenheit: "; float_view fahrenheit];
                hbox [label "degrees Celsius: "; float_view celsius]]]) in
    run_dialog (main_window, main_window#close)

The temperature entered by the user is instantly converted to both degrees Fahrenheit and degrees Celsius. (Note the definitions of "float_entry" and "float_view", although trivial, have been left out to save space.)

Though O'Caml RT is far from complete I made a prerelease because I want to solicit feedback from others who are interested in functional reactive programming. I made a web page providing links to the source code, documentation, and (coming soon) examples. It's known to compile on Mac OS X and Linux, and it should be possible already to write many simple GUI applications using it.

My questions to you are:

  • Am I duplicating work? I know of systems such as Fudgets (arrow-based) and SuperGlue (object-based); would I be better off to use one of those as a model than to create my own?
  • What would you want to see in a functional-reactive GUI toolkit? i.e. what things which are often awkward to express in procedural langauges do you think should be made easy in O'Caml RT?
  • What are some examples of things which are easy in procedural languages but you think would be awkward in a functional-reactive setting?
  • Is the API clear and understandable? e.g. does the above example make sense (even to those who don't know O'Caml)?

Thanks in advance for your feedback!

(P.S. I don't want this to come off as a plug for O'Caml RT... if any of the admins feel this is too specific a topic for LtU I'll try to find a more appropriate forum to discuss it in.)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

function based

Not to toot my own horn (toot, toot), we have what I believe you call a function based version for html/javascript: flapjax - I wrote the core library so that programmers can either lift manually (as in your case) or use the compiled version that lifts for you (credit there to michael greenberg!). Greg (FrTime) has been keeping us somewhat honest and playing with the server; ocaml support there might be really sweet.

The library based approach seems to be the way to go in our experience - everyone I know messing with the system prefers to 'think' in javascript, though looking at flex/oz/laszlo/dynamic properties, I don't think that really carries through to code templates.

I just finished an ocaml nlp project that I wanted to visualize, you should have posted sooner :) Breaking the LtU rules, I'll take a look after posting this..

- Leo

Of course!

Of course, I knew I was forgetting something :) Yes, flapjax is what I consider a "function-based" FR system, especially also being modeled after FrTime (the demos are very easy for me to follow!). Now that I'm on break I hope to have time to play around with flapjax and learn how you may have tackled some of the same problems I'm having, especially things like performing asynchronous communications from within an event. (I'm trying to find a way to model modal dialog boxes as functions which could be applied to events without blocking the entire library.) Not to mention bringing my naming scheme up-to-date with yours; I've been trying to keep my API familiar to users of extant FR systems (especially FrTime).

one more

I was trying to narrow down a class of combinators tonight when I stumbled across a first-class event system released in F# earlier this year. It might provide useful contrast implementation-wise given both are in ML and thus have similar implementation constraints.

f# imperative reactive programming

I'm not quite sure what the stumbling block is with asynchronous events so I may be misinterpreting your statement. In FrTime, no new event is popped off the event queue until the dependency graph has stabilized (the previous event has been completely handled so the topological evaluation priority queue is empty). I think parallel or alternating processing of events is possible on the dependency graph if you maintain the invariant that at any step of execution, all older events are being processed higher on the graph than new events - though this may get complicated (and undecidable) due to graph restructuring nodes (like Switch). The desired atomicity of event handling really determines what you can do in terms of concurrent evaluation. I can envision limiting the system without too much harm in practice to a variant of switch whose height is fixed assuming callbacks/messagepassing is permissable (as they should be in an 'imperative' reactive system).

Your example of a modal box, in FrTime and Flapjax, would cause a pop up, and the graph would then stabilize, so the system would then be waiting for more user input and thus also allowing other events in. Perhaps you meant the modal window should prevent some interactions but not all? lift_e(alert, timer_e()); would make an alert of the time every second in the library version, and alert(seconds_e()); in the compiled one. In my experience, io, recursive forms, and collections have been the tricky parts :) We have a couple of neat ideas on collections (barely half-implemented) and I'm mostly concerned with atomicity, optimization, and selectors at this point...

What about in the case of

What about in the case of lift_e(confirm, timer_e()), where the program requires the result of the dialog box in order to finish its computation (and, as you point out, to finish restructuring the graph in the case of switch nodes)? I presume[1] incoming events such as timer ticks are postponed until the dialog box is closed (this is what O'Caml RT currently does). In simple GUI applications this is not a problem, but what if the dialog box itself is built from FR components? The components of the dialog box would remain unresponsive until the dialog box is closed! The only solution I can think of to this problem (besides ditching the dialog-box-as-function model) is to allow signal transformers to return an "incomplete" value which can be filled in at a later point (much like futures), but this seems like it would be much more work that it's worth.

Regarding recursive forms, O'Caml RT currently only allows "recursion" in the sense of forward references, not in the sense of actual recursive signals which form a loop and execute until they settle on some value. If the recursive combinators are used to create such a loop the result is undefined. I've (so far) found this restriction not to be limiting.

[1] I'm currently stuck using dial-up on an old Windows machine so I cannot test this in Flapjax; please correct me if I'm wrong!

blocks

Flapjax *should* also block if just lifting the confirm call as that is the semantic of confirm and so I'd argue that's the expected behaviour of lift.

An asynchronous_lift, which would take a blocking call and make it non-blocking, would conceivably act like a FrTime delay in that a new hidden node would be created at the bottom of the graph and the result of function application would come at some nondeterministic time later. This usecase is what makes ajax calls natural in flapjax :) In the asynchronous_lift case, given that evaluation in FrTIme is actually single-threaded, a new thread would have to be introduced to perform the evaluation. The engine would now also have support use by multiple threads, but all the means is a synchronized incoming event queue.

In some implementations of javascript, it seems that events are handled in their own threads, so spawning a new thread to handle the event might even work in flapjax. For example, an asynchronous function lift for one reactive argument in Flapjax:


var async_lift_e = function (fn, arg_e) {
var result_e = new Event(); //node with no dependents
lift_e(function (flat_a) { setTimeout( function () { result_e.sendEvent(fn(flat_a)); }, 0 ); }, arg_e);
return result_e;
};

async_lift_e(confirm, timer_e());

where sendEvent injects an event into the event stream and setTimeout(fn, t) will evaluate fn in its own thread t ms after invocation.

Clearly, this would be easier in ocaml as it wouldn't be a series of giant browser-specific hacks; I envy you there :) More generally, allowing control to safely relax the frtime style evaluation order would be useful in terms of performance and specifying modal events. As a solution to a different problem, last summer one of us wanted to try something in an almost STM style, but we never went forward with that line of thought. I think there are clean ways to achieve concurrency/atomicity/blocking in this domain. Hopefully I'll be presenting one as part of my ugrad thesis in a few months :)

Your profile has a link to a wpi website - any chance you worked with Dan D. or Kathi F.?

async_lift makes perfect sense

I think async_lift is exactly the function I need... I initially had been trying to avoid a solution like that because the ordering of events gets lost (since the event coming out of async_lift occurs at a later time than the event going in) but it seems now like the right way to go, so long as these semantics are made explicit. Thanks for the insight! (BTW please post a link to your thesis when you finish, I'd like to see what other results you come up with.)

Your profile has a link to a wpi website - any chance you worked with Dan D. or Kathi F.?

In fact I've worked with both Dan and Kathi; Dan advised me on a project and it was Kathi who introduced me to FrTime in the first place :)

This looks great

and very useful. I'm unaware of anything else like it in the OCaml world, but if you wanted to be sure, the best way would be to post to the caml list...

Advice taken!

Advice taken!