Writing HTML apps in Haskell - Part 2
In part 1 I described the basic two-process setup for accessing the DOM from within Haskell.
In this instalment I will walk through several other crucial ingredients for a proper Haskell HTML app.
First, here’s a GIF of an animated DOM rendered in Haskell that can be modified with developer tools.
Browsers usually fetch data form the network via HTTP or one of its successors (http2, quic). The URL looks like
http://localhost/resource. WebKit supports registering custom URL schemes so we don’t have to hit the network and run a server. We instead register a
haskell:// schema with a
IO () callback which gets executed each time the rendering engine loads a resource.
It’s important to register this URI schema before creating an actual render view but the code itself is pretty simple:
webContextRegisterUriScheme context "haskell" $ \request -> do
I adapted some code from Bodil Stokke to implement a virtual DOM.
The code for the GIF above can now be written like this:
testVNode :: Int -> VNode
And at some later point you call
patch to blast the virtual tree from
testVNode into the real DOM:
patch api body oldDom newDom
CSS preprocessors are another important component of modern web development. These preprocessors like less and sass take away some of the pain of CSS. They allow e.g. defining variables for common colours, or take care of writing the correct browser-specific prefixes for some CSS properties.
Including the sass file works as usual, except that we’re using the
haskell:// scheme, and that hsass is re-processing on every reload.
<link rel="stylesheet" type="text/css" href="haskell://bootstrap.css">
- The current code can be found here.
- GObject and also WebKit require running code that modifies the DOM in the GObject main loop. So you can use all of Haskell’s runtime but DOM modifications have to be run via
- Building the separate-process plugin requires manual linking against the Haskell runtime, e.g with
-lHSrts_thr_l-ghc8.0.1. Note the
_l. Those two mean that we’re linking against the runtime that supports threading and event logging. The normal
-eventlogflags don’t work for some reason.
- I want to use hpack but it doesn’t support enough Cabal features yet.
- The compile & link times are crushing.
- The function names are auto-camel-cased from the GTK names which often leads to weird capitalisation, e.g.
I’m going to clean up the code to make it more like a library with a few entry points. If someone with stack-knowledge wanted to stackify it I’d be eager to help.
I’m surprised how well this works. Writing a UI to render some home automation data took me less than an hour. I hope I can convince others to join in to make this a more generally useful project!