Providence Salumu
We recently looked at the Snap web framework for building web applications in Haskell, but I never got round to covering templating and generating HTML. In today’s post, we’ll take a look at a somewhat different approach to generating HTML that doesn’t use templates, but in fact uses Haskell itself.
The blaze-html
library, predominantly written by Jasper Van der Jeugt for Google Summer of Code 2010, is a “blazingly fast HTML combinator library” for Haskell. This means that it provides a collection of primitives to build up HTML documents inside Haskell code. Going straight into an example, here’s how documents generally look:
greet :: UserName -> Html
greet userName = H.docTypeHtml $ do
H.head $
H.title "Hello!"
H.body $ do
H.h1 "Tervetuloa!"
H.p ("Hello " >> toHtml userName >> "!")
As you can see, the main abstraction is do-notation - Html
is actually a type synonym for MarkupM
, which is a instance of Monad
. This lets us build up a HTML document in a familiar hierarchical manner. On top of that, I’m using the OverloadedStrings
extension in GHC to automatically convert string literals into Html
- typing "Hello!"
is the same as typing toHtml "Hello"
, but far more convenient! The H.
stuff is there because I usually import Text.Blaze.Html5
qualified.
Not only does blaze-html
look natural and familiar, because it’s Haskell code and doesn’t introduce a huge amount of new data types, a lot of the things we already know immediately carry forward to blaze-html
too! For example, suppose we want to insert <hr />
between paragraphs:
addHr [] = mempty
addHr [p] = p
addHr (p:ps) = p >> H.hr >> addHr ps
Now we can easily use our combinator to build up more complicated documents:
doc = H.docTypeHtml $
H.body $
addHr [ H.p "Hello, world!"
, H.p "How are you?"
]
It’s exactly this type of refactoring that we already do in our code day-to-day, so why not apply it to rendering HTML too?
There are sadly a few drawbacks to blaze-html
- notably it is not a “true” monad (it violates the monad laws), nor is it a monad transformer. It would be fantastic if it was a transformer, as we’d then be free to use a Reader
monad as our base monad, which might provide a nice abstraction to passing around common variables in templates (e.g., the currently logged in user). That’s not to say these things are impossible - you can always layer Reader
on top of Html
, but it just becomes a tad harder to work with.
Anyway, blaze-html
remains my go-to choice for templating small web sites, because I have to learn practical nothing, now that I’ve got a good grip on Haskell! If you’re focusing on learning Haskell over the holidays, and would like to see how far you can go without learning other languages, I highly recommend blaze-html
. Even if you’re not using Haskell, maybe the ability to refactor your templates just like ordinary code is convincing enough!
You can contact me via email at ollie@ocharles.org.uk or tweet to me @acid2. I share almost all of my work at GitHub. This post is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
I accept Bitcoin donations: 14SsYeM3dmcUxj3cLz7JBQnhNdhg7dUiJn
. Alternatively, please consider leaving a tip on