However, after several years of using that, I’ve come to write my own. I’ll cover the infelicities about Blaze and then discuss my alternative approach.

Reading back through what I’ve written below, it could be read as a bit attacky, and some of the issues are less philosophical and more incidental. I think of it more that the work on writing HTML in a DSL is incomplete and to some degree people somewhat gave up on doing it more conveniently at some point. So I’m re-igniting that.

The combination of having a need to write a few HTML reports and recent discussions about Blaze made me realise it was time for me to come at this problem a-fresh with my own tastes in mind. I also haven’t used my own approach much, other than porting some trivial apps to it.

Blaze

Names that conflict with base

The first problem is that Blaze exports many names which conflict with base. Examples:

div, id, head, map

The obvious problem with this is that you either have to qualify any use of those names, which means you have to qualify Blaze, and end up with something inconsistent like this:

H.div ! A.id "logo"$"…"

Where H and A come from importing the element and attribute modules like this:

importqualified Text.Blaze.Html5 as H
importqualified Text.Blaze.Html5.Attributes as A

Or you don’t import Prelude and only import Blaze, but then you can’t do a simple map without qualification.

You might’ve noticed in the old xhtml package that thediv and identifier are used instead. The problem with using different names from the actual things they refer to is that they’re hard to learn and remember, both for regular Haskellers and newbies coming to edit your templates.

Names that are keywords

This is a common problem in DSLs, too. In Blaze the problem is: class or type (perhaps others I don’t recall). Blaze solves it with: class_ or type_

Again, the problem with this is that it is inconsistent with the other naming conventions. It’s another exception to the rule that you have to remember and makes the code look bad.

Conflicting attribute and element names

There are also names which are used for both attributes and elements. Examples are style and map. That means you can’t write:

Additionally, this instance is too liberal. You end up getting this warning:

A do-notation statement discarded a result of type GHC.Prim.Any

Suppress this warning by saying _ <- "Example" or by using the flag -fno-warn-unused-do-bind

So you end up having to write in practice (again, taken from a real Blaze codebase by one of the authors):

void "Hello!"

Which pretty much negates the point of using IsString in the first-place. Alternatively, you use -fno-warn-unused-do-bind in your module.

Working with attributes is awkward

The ! syntax seems pretty convenient from superficial inspection:

link ! rel "stylesheet"! type_ "text/css"! href "screen.css"

But in practice it means you always have the same combination:

div ! H.class_ "logo"$"…"

Which I find—personally speaking—a bit distasteful to read, it’s not far from what we saw in the old xhtml package:

thediv ! [theclass "logo"] <<"…"

Did we really save that much in the attribute department? Operators are evil.

But mostly presents an editing challenge. Operators like this make it tricky to navigate, format in a regular way and do code transformations on. All Haskell code has operators, so this is a general problem. But if your DSL doesn’t actually need these operators, I consider this a smell.

Attributes don’t compose

You should be able to compose with. For example, let’s say you want to define a re-usable component with bootstrap:

container inner = div ! class_ "container"$ inner

Now you can use it to make a container. But consider now that you also want to add additional attributes to it later. You can do that with another call to with:

Apart from the import backflips you have to do to resolve names properly, you have at least three imports to make just to render some HTML. Call me lazy, or stupid, but I never remember this deep hierarchy of modules and always have to look it up every single time. And I’ve been using Blaze for as long as the authors have.

Transforming

A smaller complaint is that it would sometimes be nice to transform over another monad. Simplest example is storing the read-only model information in a reader monad and then you don’t have to pass around a bunch of things as arguments to all your view functions. I’m a big fan of function arguments for explicit state, but not so much if it’s the same argument every time.

No Show instance

It would be nice if you could just write some markup in the REPL without having to import some other modules and wrap it all in a function just to see it.

Lucid

My new library, Lucid, attempts to solve most of these problems.

Naming issues

Firstly, all names which are representations of HTML terms are suffixed with an underscore _:

p_, class_, table_, style_

No ifs or buts. All markup terms.

That solves the following problems (from the issues described above):

Names that conflict with base: div_, id_, head_, map_, etc.

Names that are keywords: class_, type_, etc.

Conflicting attribute and element names: solved by abstracting those names via a class. You can write style_ to mean either the element name or the attribute name.

Inconsistency is difficult and ugly: there’s no inconsistency, all names are the same format.

No import problems or qualification. Just write code without worrying about it.

How it looks

Plain text is written using the OverloadedStrings and ExtendedDefaultRules extensions, and is automatically escaped:

Duplicate attributes are composed with normal monoidal append. Note that I added a space in my definition of container anticipating further extension later. Other attributes might not compose with spaces.

Unceremonious

Another part I made sure was right was lack of import nightmare. You just import Lucid and away you go:

If I want to do more advanced stuff, it’s all available in Lucid. But by default it’s absolutely trivial to get going and output something.

Speed

Actually, despite having a trivial implementation, being a real monad and a monad transformer, it’s not far from Blaze. You can compare the benchmark reports here. A quick test of writing 38M of HTML to file yielded the same speed (about 1.5s) for both Lucid and Blaze. With such decent performance for very little work I’m already ready to start using it for real work.

Summary

So the point of this post was really to explain why another HTML DSL and I hope I did that well enough.

The code is on Github. I pushed to Hackage but you can consider it beta for now.