Writing HTML apps in Haskell - Part 1
Recently I wanted to to build a user interface in Haskell that runs on my Gnome desktop, not in the cloud (the audience gasps).
The obvious choice was to use a toolkit, so I tried qtah-qt5, gtk3 and gi-gtk. [ … ]. I had a fairly long section here on my experience but it was unnecessarily mean, so I replaced it with a representative image:
I played with Electron but having written a large app in TypeScript already I decided that I don’t want to give up my Haskell data structures again. The problem I’m trying to solve is too complex to be coded without QuickCheck and types.
The summary: It works! - but it’s not yet straight forward.
The handler that renders the content above looks like this:
testClosure2 :: DOMElement -> DOMMouseEvent -> IO ()
The dependencies are non-trivial, so I set up a project with a nix-shell. You can find the intermediate, messy state here.
To access the DOM however we need to run in the worker process. The only way this can be done is by exporting a symbol with the name
webkit_web_extension_initialize_with_user_data. I implemented a small c shim for that.
The full flow now is:
- Implement a main process that opens a window and embeds WebKit.
- Implement a shared library that connects to the
page-createdevent and hands off the flow to a Haskell function that we implement.
- In the main process point to the directory that contains our
test.solibrary from step 2.
Now when we run our main binary each new frame loads and runs our plugin code.
- The GObject introspection support is very thorough, but AFAICT it doesn’t support callbacks for DOM events. The callback code currently requires a bouncing through c code.
- The compile is slow
The setup looks complicated but I think it’s worth the effort. UI development with CSS, a web inspector, and retained-mode, virtual-dom libraries like React and Vue is vastly superior to the toolkit-style development. These UI libraries have a gigantic mind-share compared to classic desktop development.
On top of that enormous resources are spent on development and optimisation of web engines. In the not-too-distant future we might see a Gtk-Servo backend that uses 3x less memory and is 5x faster.