GUI libs? No thanks, I’m just browsing

A recent thread on the haskell-cafe reminded me of an old experiment of mine, and that it could still be useful for current Haskellers.

The idea was to use a web-browser as a GUI for Haskell programs running locally, via a rudimentary HTTP-server on the Haskell-side and some AJAX on the browser-side. Even in 2007, that was not a new idea, but with web development often being somewhat separate from other development, and non-web developers being quite attached to their GUI lib of choice, it still is not as widely known as one might think.

The advantages are quite compelling, and getting more so as rich internet applications are becoming commonplace, web standards take on the best of browser and library experimental features, and all active browser implementors are finally working on implementing those standards. To mention just a few:

  • unlike GUI libraries, browsers are not linked to your code – you do not have to rebuild the browser when you update your GHC
  • if you stick to implemented standards, you and your users can choose which browser they prefer (if they know web development, they might be able to tune and style your GUI)
  • not only are your GUIs portable accross platforms, there are browsers that stick to whatever look and feel is native to that platform
  • with web-based platforms making strong inroads against locally installed software, some users will find their browser’s look more native than any single OS’s

Some of the (potential) disadvantages:

  • you’ve got a network connection between your application and its GUI (you need to secure that, there might be performance limitations)
  • in spite of progress, not all modern standards are yet supported equally well in all browsers

Unlike real web developers, you won’t have to support older, non-standards-compliant browsers – just ask your users to install a modern browser of their choice (they are smaller and easier to install than most GUI libraries). You might of course get requests to support your application on a mobile browser where Haskell hasn’t yet been ported to the underlying platform, but then you could at least run your Haskell application on a server and connect a mobile GUI to it!-).

[screenshot of JustBrowsing in Opera][screenshot of JustBrowsing in Firefox][screenshot of JustBrowsing in Safari]

To make things concrete, here is a silly little Haskell code example, not pretty (and Canvas isn’t new anymore, either;-), but demonstrating many of the ingredients. The code needed only minor updates (exception handling has changed, and some modern browsers seem to prefer [::1] instead of localhost). It doesn’t do anything useful, instead it provides simple examples of the common issues:

  • integrate a network server into your application (since we only need the most basic features of an HTTP server, I just coded them explicitly, on top of the network package). We have a server loop, which parses HTTP GET requests, dispatches to Haskell functions based on URL, and delivers HTTP replies based on the function results.
  • the simplest GUI is just an HTML page served at some known URL (for our purposes, a named server entry point).
  • GUI elements, such as buttons, can use XHR/AJAX to call into other URLs (the get/callback pair of Javascript functions).
  • Haskell code can generate Javascript code that will be evaluated browser-side, in order to manipulate the HTML page. As an example, Haskell function draw recursively computes Javascript instructions that draw on a HTML canvas (in real applications, one would avoid the general eval, writing a custom interpreter loop specifically for graphic commands instead).
  • While the draw example delivers a whole bunch of changes at once, addLabel gives an example of continued interaction (each browser-side click gets another label text from the Haskell server and adds it to the HTML page).
  • One typical scenario that is not demonstrated are server-initiated events (say the server was computing a feed of fibonacci numbers in the background, pushing them browser-side whenever ready). This has typically been handled by having the browser making an asynchronous server request that only returns when the server has a message to send, and is reestablished immediately (so there is always a browser callback waiting when the server has something to send).

To make this example self-contained in a single file, I chose to hack up my own HERE-file support (function commentData), so that I could include the GUI HTML and Javascript to be served in the Haskell source file, as a comment at the end. The additional thisFile hack enables the code to figure out its own file name, when run via runghc — if you hardcode the source file name instead, the rest of the code is portable Haskell (which wouldn’t have been the case if I had used Hugs’ HERE-doc support, or GHC’s quasiquoting equivalent).

These days, one should package the whole application as cabal package, managing both the dependency on the network package and any HTML pages or Javascript files one might want to include as data, without the HERE-file hack.

To try just run main in Canvas.hs (for the single-file version; needs package network installed), or unpack the tarball and cabal install the package, followed by running JustBrowsing (for the cabal-package version). Then point your browser at http://%5B::1]:8000/start.

Other things to consider in modernizing: HTTP packages on hackage, websockets instead of HTTP, standard server-sent events.

This entry was posted in Uncategorized and tagged , , . Bookmark the permalink.