new website, and the Bad Shape

Starting with my last stream, over the last week I've been hyper-focused on improving my website. I've lost quite a bit of sleep and missed a few meals, but I'm quite happy to say that my website is now written in Haskell, powered by the Shake and Pandoc libraries, and is verifiably Very Cool.
The website used to be powered by Quartz and Nix, a setup that I found myself frequently frustrated with. The Nix part of the project was fine -- though I still need to learn Nix more fully. The Quartz part of the website, however, was a major source of the frustration. It has the Bad Shape.
the Bad Shape
In general, with programming, there's a particular kind of "shape" that I really dislike. This shape is hard to understand, hard to maintain over long periods of time, and hard to customize. I like to call this "shape" the Bad Shape, and I've briefly written about it before. This is in contrast to the "pipe" shape, which is easy to understand, easy to maintain, and easy to customize. I think pipes are the best kind of shape for a program to have.
Visually, the difference between the two looks like this:

I've found myself showing this particular image to others a lot when I explain my design sense when it comes to programs and APIs. When you're programming, you can imagine that each part of the program is a piece that can snap neatly into other pieces, like Lego bricks or sockets.
I find the process of creatively snapping pieces of functionality together to be really enjoyable. With pipes, as long as the input and output types match, you can connect them or change them in or out for other pipes without any issue. You can do whatever you want at any step along the way.
The problem comes when you need to snap pieces together that need predefined shapes. The Bad Shape is when you have some kind of "wrapper" around the pieces that you want to use. No longer can you snap together whatever piece you want that happens to have a matching type; now, because the Bad Shape defines both the input and output constraints, you have to make the exact piece that the Bad Shape wants.
concrete example
What this means, concretely, is that the Bad Shape is harder to use. Consider, for example, this typescript code from my old website:
let repo: Repository | undefined = undefined
return async (_tree, file) => {
let date: MaybeDate = undefined
const fp = file.data.filePath!
const fullFp = path.isAbsolute(fp) ? fp : path.posix.join(file.cwd, fp)
for (const source of opts.priority) {
||= file.data.frontmatter[source] as MaybeDate
date
}
.data.dates = {
file: coerceDate(fp, date),
created: coerceDate(fp, date),
modified: coerceDate(fp, date),
published
} }
In brief, what this function does is extract date information from a Markdown's frontmatter. However, it's shaped in a very specific way:
- It only takes a
file
as input, which is a parsed Markdown file. - It changes the state of the
file
to have the same date for thecreated
,modified
, andpublished
date. - It doesn't return anything.
It's written this way because that's how
"transformers" are defined in Quartz. I have no
freedom to change the input or output types, and
furthermore I have to understand the
machinations of Quartz in order to
implement this function. This partially explains why
I return the same value for created
,
modified
, and published
--
it's because of what Quartz does with those dates
later, after my function has run.
Suddenly, I'm required to understand the changing mutable state of the entire Quartz program. This happens whenever Bad Shapes are involved. Instead of being able to focus on the individual transformation I want to make, I now have to understand much more about what the program does before it gets to my function and what it does after my function runs.
This increases the mental workload, increasing the difficulty of writing and maintaining the program. It also becomes less fun!
counterargument
Of course, I would be remiss not to mention that
Bad Shapes are sometimes necessary and "good,
actually". If you're already familiar with Quartz,
you might notice that the code I posted earlier
looks a lot like a
QuartzTransformerPlugin
, and that's
because it is. Bad Shapes like to turn into the
plugin pattern, and sometimes plugins are a good
solution to a problem despite the fact that they
impose limitations on what you can do.
However, "avoid bad shapes" is one of the design principles that I try to follow, along with "avoid shared mutable state". You can't avoid either completely, but I think they're good rules of thumb.
This is why I like functional programming so
much; since most libraries and functions are just
"pipes", it lets you compose functionality easily,
increasing how enjoyable it is to write programs.
Another example on the "good" end of this spectrum
are unix-style commands. cat
prints a
file, tail
lets you get the last few
lines of the input, and cowsay
lets you
print an ascii cow saying whatever the input
was:
$ cat content/support.md | tail -n 1 | cowsay
_________________________________________
/ I also love it when you share the stuff \
| I've made that you like with your |
| friends and post nice comments on the |
\ things I make. <3 /
-----------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
This works because the types match! And you can change up the pipeline as much as you want.
it's Bad Shapes all the way down
However, static site generators (or SSGs for short) are particularly rife with this problem. If you try to find a static site generator to use, they all prescribe some kind of workflow, because they're trying to get you to provide the pieces that the Bad Shape wants. I feel like SSGs like to embrace the Bad Shape... and it makes me feel disappointed in software. You end up having to spend a lot of time reading the SSG documentation to know how things work so you can make it do what you want, and that sucks! It's like you have a box that does... something, and you just have to pull random levers until it starts working. Writing or using software could be a lot more fun if I didn't have to do that all the time.
Quartz, in particular, takes this to an extent
that I found a little hard to believe when I first
started using it. It does the typical things that
most other SSGs do, like requiring that you put all
of your content in a specific /content
folder. But, it takes the Bad Shape pattern even
further. To use Quartz you have to literally clone
the repository and make your own changes ontop. I'm
not making this up, that is how they tell users to
get
started:
Then, in your terminal of choice, enter the following commands line by line:
git clone https://github.com/jackyzha0/quartz.git cd quartz npm i npx quartz create
The content folder, custom plugins, custom components, even the configuration file have to be modified directly inside of the repository. Hopefully this makes it abundantly clear why I didn't like working with Quartz.
what's changed
The website looks very similar to what the site looked like a week ago -- the styling is mostly the same, but as you may have noticed by now, the biggest visual change is that elements on the page are actually aligned with the dot grid.
On the old website, the dot grid was static and didn't move when you scrolled the page. Elements on the page also didn't have a grid size. Now, almost1 every single element on the page is sized in an exact manner such that everything stays aligned to the grid, through to the bottom of the page. This helps invoke the sense that you're reading a dot grid bullet journal, which was the original desire for the background. If you're interested in seeing how this is done, the style.css is thoroughly documented.
Data is also generally more consistent now.
Almost2
every page has well-formed and well-defined
timestamps, every tag uses kebab-case
,
and every index page contains a listing of files --
including the home page.
There are several things that are missing, notably:
- No search function.
- No description in RSS feeds.
- Backlinks are missing.
- Code syntax highlighting.
- No dark/light theme toggle.
I plan on fixing these issues slowly over time. However, the software I wrote to build this website is much easier for me to maintain and extend, opening the doors for further modifications and features which were previously impossible or difficult to do:
- Unify the VOD website with this website, so notes can link to VODs directly and vice-versa.
- Compress/resize images on build to reduce the size of pages.
- Check to make sure that there are no broken internal links.
- Add RSS feeds for every tag, so you can get notifications about a specific thing if you want.
- Export the notes in different formats, like the original Markdown source and JSON.
But, I think a week of near-constant working is enough for now. I need to get back to taking care of myself, telling people that my game no signal is coming out, and looking for jobs.
I hope you enjoyed this write-up and like the new website! I'm looking forward to making the site better in the future ❤️