Because of the COVID-19 pandemic, I have been staying in for the last few weeks. Suddenly I had some free time so naturally I tried to reboot my blog That seems to be the case for many others as well, I have seen a lot of people restarting their blog and Youtube podcasts, for example Chris Coiyer and Remy Sharp, whom I have been following for a very long time.
. Since Haskell is my go-to programming language now, this time I decided to use Hakyll to build my blog with. In the past I have tried quite a few static site builders like Jekyll and Hugo, however they are not flexible enough to my use, hence I always seem hit their limitation as I want a lot of flexibility and customization, which with Hakyll I can achieve. Sure I will need work through Hakyll’s own rather complex source first but I would not mind it at all, especially compared to the similarly complex, if not more, of Jekyll.
I have been quite satisfied with the power Mainly because of Pandoc. It truly is the universal converter which support the most extensive number of formats as well as a huge number of plugins.
and flexibility of Hakyll Another valued feature of Hakyll is the flexibility with custom routing, instead of requiring the files to be laid out exactly like the resulting site. This feature seems to be inspired by Nanoc.
, while I still think the context mechanism and dependency management is overly complicated, the area where I felt most dissatisfying is Hakyll’s template system. It gets the job done but it feels rather complicated and not quite familiar. That is why last weekend I decided to sit down and give it ago trying to add Mustache template support to Hakyll.
In Haskyll, you build the final files using compilers, typically, a skeleton project generated by Haskyll will contain code snippet like this:
How it works is that in the first rule,
templateCompiler will read the template files, parse it and build an internal
Item Template, ready to be used later In Hakyll, everything is an
Item. A file you read from disk to process will be an
Item String, through compilers and transformers you can turn that initial
Item String into
Item a, where
a can be anything to your liking. As long as there is a
Writable instance for
a, you will then be able to write it to form your final resource.
. The second rule does something similar, it first loads the posts as
Item String, then through
pandocCompiler turning into
Item Text, with the text here is the rendered Markdown. What is interesting here is
loadAndApplyTemplate, what it does is to build a “context” using the post’s
Item, then take the pre-compiled template file
"templates/post.html" and substitude the placeholders in the template with the value coming from the context. This seems very straight forward and is a nice API, so my plan is to provide a compiler and my own version of
loadAndApplyTemplate In reality, there are a couple of other functions that Hakyll provide, which I also need to implement, for example
applyAsTemplate. Nevertheless, they are similar enough and are built from the same core, so it is trivial to write one given the other.
that works with compiled Mustache templates.
Turns out that can be done in less than 100 lines of code. You can take a look at the final module on Github. A couple of notes:
I used stache, however I could not support
functionFieldcontext field with it, so that’s a lost in term of feature parity. It’s not a problem for me because I want my template to be logic-less anyway, any field value processing and transform can be done by adding a custom field with the transformed value. And if I really wanted to have
functionField, I could easily switch to mustache, which does support Mustache lambda extension.
Because Hakyll use
Binaryto serialize items to disk, I am required to implement a
Template. GHC was able to derive most of the instances for me, except for
Posfrom Megaparsec. But since
Posis just a
Int, I was able to wrap/unwrap it to provide a
Contextis a quite complicated type. Eventhough was able to look at the source of Hakyll and figure out how to lookup value from it I still am not sure what of its all the arguments does.
Context, I could not find a way to list all the keys from a particular context. What I end up doing is to traverse the compiled template’s
Node, looking for variable and section fragment and look them up from the
Context. It turns out to be perfectly fine, it works as expected and when the template references some non-existing keys in the context, the original (and sensible) error message will show up.
It was a fun journey, I learned a ton. Looking forward to spending more time hacking Hakyll. I have a couple of features I want to implement for this blog, namely multilingual and search When researching static site builders, I came across Zola. It is a very nice static site generate, with tons of features some I did even imagine possible with static site generators. I choose to go with Hakyll because of Pandoc, and the pandoc-sidenote extension that turns footnotes into side-notes, displayed in a nice Tufte layout.
, so stay tuned!