updated draft on achille 2.0

This commit is contained in:
flupe 2022-12-09 15:28:09 +01:00
parent 6e1302fb3c
commit f8b1c385f0
2 changed files with 98 additions and 20 deletions

View File

@ -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 {

View File

@ -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: