Tue, 25 Apr 2006
GTK+ Callbacks in OCaml.
GUI programming is one of those areas thats always a bit of a pain regardless of language used. Ocaml however has some language features (not present in C, C++ Java, Python etc) which make GUI programming in Ocaml somewhat more elegant.
Regardless of the language used, GUI programming means writing a bunch of small functions (or class methods in OO systems) that get called when the GUI elements are manipulated by the user of the application. These small event handling functions are often called callbacks and are usually associated with the GUI widget (button / menu / whatever) when the widget is created. For example if C/GTK+, to catch the mouse button click even in some widget w the programmer has to define a button press handler with a function signature like:
static gboolean button_press_callback (GtkWidget *w, GdkEventButton *ev, gpointer data) ;
When the program's user clicks the mouse button in the widget, this function will be called with a pointer to the widget as the first parameter and a pointer to the button event data (cursor x/y position, time etc) as the second parameter. The third parameter is a generic pointer. At the time the callback is registered, the programmer can set this pointer to be a pointer to anything he/she wishes and then cast it to the right type in the callback. One common usage is to set this as a pointer to a struct containing the current state of the application, so that this state can be modified within the callback function.
One common problem I have always run up against when doing the above is when I have more than one instance of a particular widget type and want to handle all of them using a single callback. If the last parameter is a pointer to the state, then how do I differentiate between the different widgets that generate the event. Yes, it can be done using the widget pointer, but thats just a pain. Usually, the best solution is to make the state data a global variable (yuck) and then make the last parameter a value which identifies which widget generated the event.
Ocaml has a really neat solution to the above problem; a programming trick called closures. In this particular context, closures allow something that behaves like a partial application of a function.
Consider the function :
let add_2 a b = a + b
For those who don't read Ocaml, this is a function that takes two integer parameters and returns their sum. Using the above function we can define a new function like this:
let add_to_x = add_2 x
As you can see, this uses our function from above, but calls it with only one parameter. So what the hell is add_to_x? Well its a closure (a type of function) that takes a single integer parameter and adds it to the value the variable x contained when the closure was created.
People with a background in OO languages can look at a closure like add_to_x as an object with a single method, but without all the overhead of defining a class and instantiating an object of that class.
So, when doing GUI programming in Ocaml we can define a callback with any number of parameters and with the last parameter as button event data:
let button_press_callback a b c d event = (* Code goes here, and then return true. *) true
and when we register the callback with create a closure of the callback function with all but the last parameter. This is a much neater way of doing things than any widget callback handling I've ever seen in C, C++, C#, Java or Python.