Are any OCaml GUI bindings under active development?

,

I’m sure this is a FAQ (shouldn’t there a FAQ btw?), but, which of the bindings for GUI libraries under active development/well maintained? I might want to build a couple of small tools for myself and I’d like to learn something that is likely to remain usable for a while.

(Alternatively, people could answer the question “if I had to write some software in OCaml featuring a GUI, currently I would use the following libraries, tools, or techniques…”)

2 Likes

Lablgtk has been maintained for a long time and it is not going away, but it binds to GTK2 which can be odd-looking in recent graphical environemnts (and may not be that actively maintained itself).

If I had to write some software in OCaml featuring a GUI and a simple web interface would suffice for my GUI needs, I would use OCaml plus web programming (I have never played with Electron or such, so I would possibly write the GUI layer in Javascript).

There’s also cuite which is a project started by @let-def to provide bindings to Qt.

1 Like

Is there any plan on porting it to GTK3?

So the “just use the web” idea (and electron) is kind of seductive, but the performance of such things is terrible, with memory usage being insane. JavaScript gives me the willies. I suppose I could use js_of_ocaml or some such, but it doesn’t fix the terrible memory usage…

Interesting, though the github project doesn’t have much in the way of documentation.

You can try https://github.com/Kakadu/lablqml. It binds to QT toolkit. It seems to be maintained so far.

2 Likes

Ocaml-efl is also actively maintained.

1 Like

I don’t know about any plans – I think it’s a decent chunk of work to write bindings, and there is maybe not enough interest in the community to justify it. Also the GTK community is constantly changing things (GTK4 is already well in the work) and has a scarce workforce (so it’s unclear that the long-term maintenance status of gtk3 is going to be an improvement over gtk2), and people want “more multi-platform” approaches today anyway…

(Adrien Nader would be one of the people in the OCaml community that is most knowledgeable about GTK, but to my knowledge he is not a Discuss user.)

Looks like there’s the start of a GTK3 branch here: http://git.ocamlcore.org/cgi-bin/gitweb.cgi?p=lablgtk/lablgtk.git;a=shortlog;h=refs/heads/gtk3.x

No activity since 2013 though.

Also: https://lists.ocamlcore.org/pipermail/lablgtk-list/2017-July/002107.html

It might be possible to to automate much of the work of building bindings by crawling header files using something like the Clang/LLVM bindings or using one of the several C parsers that people have built. I’ve used this technique in other situations and it has worked well.

That said, it is easiest to do this if you understand the way the library you’re working with (GTK in this instance) works well enough to understand how to transform the C bindings into something for your target language, which is not always trivial. The advantage, though, is that as the target updates itself, you can mostly just re-run the binding generator.

BTW, I was mostly musing there, I don’t want to suggest anyone should be doing more volunteer work than they already are doing. I know from experience how unpleasant it can be to have created something as a volunteer and then to have people irritated that you’re not volunteering even more of your life for their benefit!

1 Like

That email explicitly says that the maintainer isn’t very interested in updating the library to new versions of GTK because of the way the GTK people have operated, which is understandable, but also makes one less interested in using the library for new development.

@perry,

I was (and I am still looking for OCaml Gtk bindings) and given that nobody was really interested in it, I created the Ctypes bindings for GObject-Introspection. I know a little about it thanks to Kouhei Sutou who is the maintainer of the ruby-GNOME2 project and because I helped him to transform some of the C bindings gem to GObject-Introspection based bindings.

My idea was to use the GObject-Introspection bindings to create a loader that will be able to generate static bindings. The Ctypes bindings are Ok (~) but the loader is hard to create and will need some time.

You can find what I have done so far here https://github.com/cedlemo/OCaml-GObject-Introspection. Please keep in mind that I am not proud of it, because I think that someone else than me could manage to do something way better but the fact is that it seems that nobody was interested in it so I had the only choice to do it.

If someone want to create better bindings for GObject-Introspection and or a bindings generator for Gtk3, I can stop working on my own bindings and help him.

2 Likes

Hi everybody,

Thank @jnavila for linking to Cuite. Mid 2017, I wanted to have bindings to a modern and cross-platform GUI library. I experimented with libui (not serious, just to get started), gtk3 (disappointing) and qt5.

Qt5 was the most pleasant to bind and use, despite being written in C++. I made a proof-of-concept for each important feature, just to be sure my approach would not lead to a dead-end. I am confident in the foundation of my binding, but more work is needed to bind the huge surface of Qt5.

@Kakadu’s work is on “Qt Quick” library (QtQuick & QML) while mine focus on QtGui/QtWidgets.
These two are different way to design UI: Qt Quick is a modern take on UI, web inspired and especially appropriate for mobile UIs (but not limited to). QtWidgets is the classic approach to desktop apps.

Cuite has been tested on linux with X11 and Wayland built with G++, and macOS with clang.

@cedlemo: too bad I didn’t know about your work. I could have helped when I was looking at gtk3. However I dismissed gtk3 for some of the reasons @gasche mentioned. Cross-platform experience was poor, I didn’t like the API and I wasn’t confident in the quality of the libraries.

If some volunteers are interested in Qt5, I can turn my proof-of-concept into an alpha version (short term work is finishing the build system, documenting the mapping of Qt concepts to OCaml and the runtime support library that allows to achieve that).
However I didn’t use Ctypes for that (it wasn’t really useful for dealing with C++, complex heaps and lifetimes).

6 Likes

What are the differences between your binding and that of Goswin von Brederlow : ocaml-qt5 ?

I will try to describe the differences by reading from Goswin’s code, however I might understand some things incorrectly.

Cuite

First, here is how Cuite works:

  • a small runtime library binds each Qt concept once and for all
  • the bulk of the library is automatically generated from a synthetic description (see QtWidgets for instance), building on the primitive concepts from the runtime library

The goal is to have as few work as possible on OCaml side (a “low-level” binding). If you want to build nicer abstractions, you can, but this foundation will be needed anyway. Qt code is exposed almost directly.

The primitive stuff that is taken care of includes:

  • exposing Qt basic types, variants, enumerations and flags
  • exposing Qt object model and inheritance, with signals and slots
  • fine-grained GC interaction, such that manipulating Qt objects is as natural and safe as possible, allowing to mix automatic and explicit memory management for deterministic resource release.

The library avoids inheritance (if you have to inherit a class to expose it to OCaml, then you won’t be able to expose objects that have been instantiated under the hood by Qt). Users of the library should rely only on composition.

One exception is for data models (abstract classes on C++ side). They are implemented by inheriting once, proxying each abstract method to an OCaml function (see Cuite__model).

For the user of the library

No C++ code has to be written at all. I also tried to make the design dead-code elimination friendly to produce small binaries (linking to a shared libcuite.so) when few features are used (still work-in-progress).

The module system is used to structure the code. Each Qt class maps to one module. All code on OCaml side is parametric: ad-hoc behaviors (same function having different effects on different objects) occur only on C++ side. Single inheritance subtyping is encoded with polymorphic variants used in a phantom type.

It gives a procedural feeling that should lead to predictable code for OCaml users (see this PoC example translated from Qt).

OCaml-like type safety is present: if you have an instance of an object, you should be able to use it safely. Places where nulls are accepted are represented as an option type. There are two exceptions to the rules (which should be clearly identifiable from the context):

  • for deterministic memory management, explicit deletion is allowed. In this case, you might get “use after free” OCaml exceptions if you reuse a value, but safety is preserved.
  • some low-level resources cannot benefit of managed memory (for instance iterator invalidation cannot tracked in general).
    In that case, you are on your own and Cuite exposes the low-level primitives only. The design is such that it should be possible to isolate the unsafe part in a safe to use OCaml module, after identifying common use cases (which requires manual curation and can be done after, at a higher-level than automatic generation).

My small experience (though I haven’t done any serious project) is that code ends up being shorter and more structured than the equivalent in C++. However, it is more explicit about recursion which sometimes require rethinking the design (“tying the knot” is not implicit as it is in C++).

Other information are available in the following presentation that was given at an OCaml meetup.

Side question: why not automatically parse headers?

Headers contain a lot of noise that is not relevant to OCaml code.

They also don’t say anything about certain semantic aspects that are relevant to OCaml side (will this parameter live longer than the lexical scope of the method? is this pointer used as input or output? are these references “real” or just a disguised returned tuple?).

For these two reasons, I preferred to resort to a synthetic description, described as an OCaml value (OCaml is actually good for manipulating symbolic representations :)), that exposes what is really necessary for generating the bindings. The original description was automatically extracted from Qt documentation sources and manually curated.

In terms of the amount of work involved:

  • mapping concepts is O(number of concepts) with a big constant factor as we have to write C++ and OCaml code, and deal with tricky control and data flow interactions, this is done
  • mapping the object hierarchy is still O(number of classes and methods) but with a very small constant factor, as it is just one entry in the description (as in QtWidget)
  • mapping models is still quite involved (manual C++ and OCaml code for each model), however there are very few models and code reuse is possible, following existing examples.

I think this is the best trade-off given the requirements I have chosen for the binding. Further cost optimisations are possible by automating description update (this time parsing the headers, or using whatever metadata can be extracted from Qt), but code review will still be necessary and I would not trust 100% automated extraction.

OCaml-qt5

It seems to me that ocaml-qt5 tries to expose Qt at a higher-level. The library doesn’t show Qt concepts directly but extend classes with OCaml specific counterparts (OWidget for instance), which leads me to worry that it will not scale (as a lot of manual work is involved), and that the resulting api might differ significantly from the original Qt one.

The implementation also deals directly with OCaml objects (using an abstract
type only to encode a nominal hierarchy on top of OCaml structural ones) which, in my opinion, results in unnecessary complication (the C code has to know about OCaml object system, while a proper separation in theory allows to do all OO stuff purely on ML side, but I might be wrong).

On the plus side, OCaml-qt5 allows method redefinitions on OCaml side (hence the inheritance-based approach).
However Qt is designed to achieve as many stuff as possible through object composition so it is rarely a problem, and nothing prevents to do the same on Cuite side (as is done with models) when it proves necessary. It seems to be the exception rather than the rule.

In the absolute, these are not wrong design decisions however they seem less practical for the goal I am trying to achieve with Cuite.

Comparing Cuite, OCaml-qt5 and Lablgtk designs:

  • Cuite implements only a procedural API
  • OCaml-qt5 implements an OO-API directly in the bindings
  • Lablgtk implements a procedural API in the bindings, then exposes both the
    procedural API and an OO wrapper in ML

I feel that it is unecessary to expose an OO-API, I don’t appreciate this style and it leads to confusion.
GObject or C++ object systems are too different from OCaml one (a class in OCaml has nothing to do with a class in C++), there is nothing to gain semantically in trying to encode one in another, so it is purely a matter of syntax and I am happy writing Module.field.
But nothing prevents implementing it or even deriving it automatically from the description.

10 Likes

See also Library for GUI?

@let-def, if one wanted to use Cutie and knew nothing about Qt, how would one start learning it?

@let-def:
Thank you for this detailed answer. I very much hope that your project will succeed. I can not wait to take a cuite for one of my projects :wink: .

1 Like

If somebody knows nothing about Qt he should start studying modern ways to create GUI and not the old ones where you need to instantiate some objects and put them as siblings of another object. This manual creation of objects (Qt Designer is not powerful enough in complex cases) really annoyed me during programming when I did some stuff in Java/Swing or QtWidgets but the latter is definitely more superior technology.

I really don’t understand why folks are afraid to touch Qt/QML. They probably do not respect good old Delphi anchor layouts (with stack layouts where they are needed), declarative languages, reactive programming and ray tracing to redraw only parts of the GUI that are visible… TT

3 Likes