The first step in setting up related posts is getting access to other posts within the post compilation function. Take the hakyll-init sample website as an example. Say we're generating our posts with some code like the following:
"posts/*" $ do
match $ setExtension "html"
route $ pandocCompiler
compile >>= loadAndApplyTemplate "templates/post.html" postCtx
>>= loadAndApplyTemplate "templates/default.html" postCtx
>>= relativizeUrls
If we look at the code to generate the archive page, which has a list of all posts, we'll see some lines like the following of loading the posts:
$ do
compile <- recentFirst =<< loadAll "posts/*" posts
we might naively try to implant this into our posts compiler like this:
"posts/*" $ do
match $ setExtension "html"
route $ do
compile <- recentFirst =<< loadAll "posts/*" -- posts to use later
posts
pandocCompiler>>= loadAndApplyTemplate "templates/post.html" postCtx
>>= loadAndApplyTemplate "templates/default.html" postCtx
>>= relativizeUrls
This, however, will produce the following error:
[ERROR] Hakyll.Core.Runtime.chase: Dependency cycle detected: some-post.md depends on some-post.md
.
This is because we've asked the post compiling function to depend upon
the list of all parsed posts, including itself, which fails for obvious
reasons. How can we get around this limitation?
Since we can't reference the post list list as it's being generated, we'll have to find a way to compile a separate list. Thankfully hakyll provides us with the ability to do just that via versions. Below we compile a separate list of posts, to which we attach the version name "meta":
"posts/*" $ version "meta" $ do
match $ setExtension "html"
route compile getResourceBody
We can then change the post loading line above to use the "meta" version of posts.
<- loadAll ("posts/*" .&&. hasVersion "meta") posts
This compiles, but introduces another issue that must be addressed.
Now that we're compiling two lists of posts using
match "posts/*"
, we'll run into an issue where we have
duplicate posts anywhere we load posts without specifying a version.
There are two ways of dealing with this: using hasNoVersion
or applying a version to both sets of posts and addressing what might be
considered a bug in tagsRules
The simpler option is to use hasNoVersion
anywhere you
aren't using the "meta" version. It will look like this:
<- (loadAll ("posts/*" .&&. hasNoVersion) posts
The only downside of this approach is that to me, at least, it feels hacky.