Lucid 2.0: clearer than before

Since my last post about Lucid, I’ve updated Lucid to major version 2.0 in a way that removes the need for the with combinator. Now, you can just write:

Example:

Here’s the (pretty printed) output:

Overloaded

Element terms are now typed like this:

Giving a couple overloaded instances:

This is similar to the variadic printf from Text.Printf , but limited to one level of variance.

Retaining old invariants

In my last post I listed a bunch of factors that Lucid should solve, I worked hard to make sure these were met in this change.

Preserving liberal term use

You can still use style_ or title_ as an element or an attribute:

Preserving encoding properties

The script_ and style_ elements still output unencoded:

With is still available

You can still add attributes to elements using with :

Convenient construction of custom elements

You can construct custom elements if needed:

But you can also construct normal elements with a custom class, so that you don’t have to use with for extending elements like our old container_ example, you can construct an element with some given attributes:

And then use it later like a normal element:

Some basic Bootstrap terms are available in Lucid.Bootstrap.

Still a monad transformer

I didn’t change anything about the monad itself. Just the combinators. So you can still use it as a transformer:

Small trade-off

One small difference is that elements that take no children always take arguments:

So you will always write:

But in practice it seems that elements with no children almost always take a number of attributes. Exceptions to that rule are br_ and hr_ , but those are quite rare. So this is a very happy trade-off, I feel. (See the ‘real examples’ at the end of this post.)

Extending elements like this is straight-forward using our usual with combinator. Example, suppose you’re sick of writing the classic input type="text" , you can define a combinator like this:

And now you can write:

Larger trade-off

Due to the overloadedness, similar to the overloaded strings example:

You have to use a type annotation in GHCi:

Otherwise you get

No instance for (Term arg0 a0) arising from a use of it

Most Haskellers won’t care about this case, but for GHCi users it’s a slight regression. Also, in some local where declarations, you might need a type signature. For example, the following is OK:

Whereas in this case:

It’s a little harder for GHC to infer this, so you add a type-signature:

Not a big deal given the benefits, but something to be aware of.

Summary

In total, I’ve made this library almost perfect for my own tastes. It’s concise, easy to read and edit (and auto-format), it lacks namespace issues, it’s easy to make re-usable terms, and it’s fast enough. The need for the with combinator was the only wart that naggled me over the past week, I knew I’d end up making some change. I’ve also covered the trade-offs that come with this design decision.

As far as I’m concerned, Lucid can rest at major version 2.* for a long time now. I added some newfangled HTML5 elements (who knew main was now an element?) and a test suite. You can expect the only minor version bumps henceforth to be bugfixes, regression tests, and more documentation.

For some real examples:

Try Haskell is now using Lucid.

As is the upcoming Haskell homepage.

And isysuclosed.com.

© 2014-11-20 Chris Done