Using Heist and Happstack

In the course of playing around with some of the newer Haskell web application stuff recently, I found that I really like the combination of Happstack and Heist. However, there are a few challenges in getting the two to play together well, so I thought I’d write up a description of how to do it.

Step 1: Getting the dependencies right

When creating your cabal file, you’ll generally need at least the following packages:

base (of course)

happstack-server, for the Happstack bits

heist, for the Heist bits

bytestring, since it’s used extensively with Heist

mtl, which is used in Happstack

monads-fd, which is used in Heist

Those last two make for a rather unhappy combination, and are the subject of the next step.

Step 2: Making mtl and monads-fd play nicely

This took some figuring out. Basically, a good bit of Heist uses the MonadIO class from monads-fd. At the same time, Happstack uses the MonadIO class from mtl. Left to their own devices, this will lead to a lot of errors that ServerPartT IO is not an instance of MonadIO. Of course it is… just not that MonadIO.

Here’s the code I eventually wrote to fix it. You’ll need a number of GHC extensions to do this.

The language extension PackageImports allows us to import modules from a specific package. In general, it’s a bad idea if you can avoid it as it can lead to fragile code… but since here we are facing the challenge of making two packages work together, there’s not another choice. We use this extension to import both the mtl and monads-fd versions of the MonadIO type class. The second language extension, FlexibleInstances, relaxes some of the Haskell98 rules regarding what kinds of instances are allowed. It is needed for the instance declaration on the last line there, and it’s pretty harmless.

Once we’ve got both versions of MonadIO and its member, liftIO, imported properly, we simply write an instance declaration making ServerPartT IO an instance of the monads-fd version of MonadIO (copying the actual behavior straight from the mtl version). Voila, problem solved.

Step 3: Porting the glue code

If you’ve worked through the Snap tutorial, you know that you can start a new Snap project by typing ‘snap init’ at the command line, and the command writes a bit of code for you. That code includes a module called “Glue” that’s largely about making Snap and Heist work together. Well, we’ll need the same code for Happstack, and have no automated command to write it for us. Not to worry, though, it’s a piece of cake to port it over, and you can tweak it as you go.

Here’s the very simple application I ended up with that uses Heist in Happstack. This is the full source code; it assumes that underneath the current working directory when the application is run, there’s a directory called “web” containing your templates, and a subdirectory of that called “web/static” containing your static files.

I honestly don’t think much else is needed. It’s a testament to both packages, I think, that they can be made to work together in around a dozen lines of code, and no added complexity is necessary except for the well-known mtl/transformers issue. By contrast, Michael Snoyman’s persistent package looks tempting as well, but I’ve avoided it so far because the list of dependencies includes hamlet and web-routes-quasi, and I anticipate some hoop-jumping there.

I’m working on a somewhat involved web site now, though, that I intend to write in Happstack and Heist, so I’ll be in a better position to answer that question in about two to three weeks.