From f8b1c385f0d1642262ddeceebea003f610247cd2 Mon Sep 17 00:00:00 2001 From: flupe Date: Fri, 9 Dec 2022 15:28:09 +0100 Subject: [PATCH] updated draft on achille 2.0 --- content/assets/theme.css | 9 ++- content/posts/achille-smc.md | 109 +++++++++++++++++++++++++++++------ 2 files changed, 98 insertions(+), 20 deletions(-) diff --git a/content/assets/theme.css b/content/assets/theme.css index 92366d0..ef0be94 100755 --- a/content/assets/theme.css +++ b/content/assets/theme.css @@ -111,12 +111,19 @@ hr { padding: 0; margin: 2em 0 0; } -#pidx li {line-height: 1.6em} +#pidx li { + line-height: 1.4em; display: flex; + margin: 1em 0; +} #pidx li > span { font-family: monospace; font-size: 14px; margin-right: 1em; opacity: 0.7; + flex-shrink: 0; +} +#pidx li a { + box-shadow: none; } details summary { diff --git a/content/posts/achille-smc.md b/content/posts/achille-smc.md index fe11195..7f0d252 100644 --- a/content/posts/achille-smc.md +++ b/content/posts/achille-smc.md @@ -1,35 +1,106 @@ --- -title: Building my site with monoidal categories +title: Generating incremental static site generators in Haskell using cartesian categories date: 2022-12-06 draft: true +toc: true --- -Or how the right theoretical framework solved the last problem I had in the way -of incremental generation for "free": reasoning about dependencies optimally. +A few days ago, I released the new version of [achille], a Haskell library +providing an EDSL for writing static site generators. This embedded language produces +efficient, *incremental* and *parallel* static site generators, *for free*. + +[achille]: /projects/achille + +In this post, I will explain how [achille] is able to tranform this intuitive, "readable" +syntax into an incremental static site generator: + +```haskell +import Achille as A + +main :: IO () +main = achille $ task A.do + -- render every article in `posts/` + -- and gather all metadata + posts <- + match "posts/*.md" \src -> A.do + (meta, content) <- processPandocMeta src + writeFile (src -<.> ".html") (renderPost meta content) + meta + + -- render index page with the 10 most recent articles + renderIndex (take 10 (sort posts)) +``` + + +Importantly, I want to emphasize that *you* --- the library user --- neither +have to care about or understand the internals of [achille] in order to use it. +You are free to ignore this post and directly go through the [user +manual][manual] to get started! + +[manual]: /projects/achille/ + +This post is just there to document how the right theoretical framework was key +in providing a good user interface that preserves all the desired properties. --- -A while back I made [achille](/projects/achille), a library for building -incremental static site generators in Haskell. I'm not gonna delve into *why* -for long, if you want the full motivation you can read the details in the -(outdated) [documentation](/projects/achille/1-motivation.html). +## Foreword -The point was: +The original postulate is that *static sites are good*. Of course not for every +use case, but for single-user, small-scale websites, it is a very practical way +of managing content. Very easy to edit offline, very easy to deploy. All in all +very nice. -- static sites are good, therefore one wants to use static site generators. -- the way to build their site becomes quite intricate and difficult to express - with existing static site generators. -- thus one ends up making their own custom generator suited for the task. +There are lots of static site generators readily available. However each and +every one of them has a very specific idea of how you *should* manage your +content. For simple websites --- i.e weblogs --- they are great, but as soon as +you want to heavily customize the building process of your site, require more +fancy transformations, and thus step outside of the supported feature set of +your site generator of choice, you're in for a lot of trouble. -Making your own static site generator is not very hard, but making it -*incremental* is tedious and requires some thinking. +For this reason, many people end up not using existing static site generators, +and instead prefer to write their own. Depending on the language you use, it is +fairly straightforward to write a little static site generator doing everything +you want. Sadly, making it *incremental* or *parallel* is another issue, and way +trickier. -That's the niche that [Hakyll](https://jaspervdj.be/hakyll/) tries to fill: an -embedded DSL in Haskell to specify your build rules, and compile them into a -full-fletched **incremental** static site generator. Some kind of static site -generator *generator*. +That's precisely the niche that [Hakyll] and +[achille] try to fill: use an embedded DSL in Haskell to specify your *custom* build +rules, and compile them all into a full-fletched **incremental** static site +generator executable. Some kind of static site generator *generator*. -## achille, as it used to be +[Hakyll]: https://jaspervdj.be/hakyll/ + +## Reasoning about static site generators + +Let's look at what a typical site generator does. A good way to visualize it +is with a flow diagram, where *boxes* are "build rules". Boxes have +distinguished inputs and outputs, and dependencies between the build rules are +represented by wires going from outputs of boxes to inputs of other boxes. + +The static site generator corresponding to the Haskell code above could be +represented as the following diagram: + +... + +Build rules are clearly identified, and we see that in order to render the `index.html` +page, we need to wait for the `renderPosts` rule to finish rendering each +article to HTML and return the metadata of every one of them. + +Notice how some wires are **continuous** **black** lines, and some other wires are +faded **dotted** lines. The **dotted lines** represent **side effects** of the +generator. + +- files that are read from the file system, like all the markdown files in + `posts/`. +- files that are written to the filesystem, like the HTML output of every + article, or the `index.html` file. + +The first insight is to realize that the build system *shouldn't care about side +effects*. Its *only* role is to know whether build rules *should be executed*, +and how intermediate values get passed around. + +### The `Recipe m` abstraction I had my gripes with Hakyll, and was looking for a simpler, more general way to express build rules. I came up with the `Recipe` abstraction: