Over the Thanksgiving holiday, I discovered cooklang and started experimenting with it. My first impression was “ewww”—I didn’t like that you couldn’t have ingredients without steps. Many of my own “recipes” are just a list of ingredients, so I don’t forget what flavors I used in a dish. The “steps” come from memory, or instinct, or whatever. Cooklang does not play well with this style of “recipe”.
But I started converting some recipes I’ve either written “correctly” to share, or found somewhere online. Although the conversion process is tricky sometimes (LLMs help a lot, but you have to do some editorial passes to get it just right), I ended up with a nice database of scripting-friendly recipes.
I like the idea of the point-of-use declaration syntax for keeping a recipe internally consistent. When making changes or writing a recipe in a hurry, it is easy to forget to mention an ingredient, or to forget to list an ingredient that is mentioned in the steps. Cooklang handily eliminates this problem. So, once I stopped thinking of it as a “Markdown for recipes” and started thinking of it as a computer language for recipes, I started appreciating it more. It really is a storage format, not a presentation format.
To really appreciate cooklang, you need good conversion tools. You need to be able to convert a “traditional” recipe into cooklang, and you need to be able to convert cooklang into a nice presentation format (where it looks like a traditional recipe). Luckily, the community has already created most of these tools. I wasn’t able to find a Markdown recipe to Cooklang converter though, so I started writing one that uses a few passes through Claude to convert and verify the conversion, plus handling additions of ingredients to your “aisle.conf” file. I don’t think my tool (which was largely “vibe coded” with Claude) is ready for prime-time yet, but if I polish it up a bit I may release it on Github later.
Honestly, this is the best solution I’ve found so far for the one thing that sends me down this rabbit-hole every single year: Creating a detailed and organized shopping list given a meal plan. Totally scriptable (and the cook tool does it out of the box). Nice. What I still miss is a really good looking PDF output option. I’m working on that too, and I have something that is about 85% of what I want. I guess that’s half the fun, right?
For background, I have been working on a pet project that involves building a single-file archive containing the directory structure used by a BorgBackup repository. The reason isn’t that important here (I’ll post about it later, maybe), but I have been evaluating a couple of things to get an idea of performance tradeoffs:
Build a tar archive with zstd compression of the original files that Borg would be backing up.
Build a tar archive with zstd compression of the Borg repository itself.
Build a SquashFS archive with zstd compression of the original files that Borg would be backing up.
Build a SquashFS archive with zstd compression of the Borg repository itself.
And then I decided to add DwarFS to the list of options, so also these:
Build a DwarFS archive with default compression of the original files that Borg would be backing up.
Build a DwarFS archive with default compression of the Borg repository itself.
The reason that I wanted to look at DwarFS is that it seems to compare favorably to SquashFS and tar+zstd in terms of both space and time (See the comparison.).
But, I found that for some reason on my testing data with my (old) laptop, it is taking an order of magnitude longer to create the DwarFS archive. The plots below show the time to create the archive for each of the three methods on the raw files (~13GiB) and the Borg repo of the same information (~8.3GiB).
Figure 1: Creation time in seconds.
The final size is a little better than the others (See Figure 2 below.), but the time it takes to get there is unacceptable. I’m sure this is a “me” problem, but I’m not sure what is causing it. I will have to dig a little deeper on this one.
In my last post (two years ago!!!), I looked at PDM as a package manager because I was feeling a little frustrated with Poetry. TLDR: It didn’t work out, and I stuck with Poetry.
But recently I’ve been using uv, and I’m hooked. It is so fast, and it has a solid solver. I also appreciate the options it has for different ways of managing a project, as well as the handy uvx (or uv tool) command to run “tools” that aren’t part of your project. I think of those as a more temporary version of what I would otherwise install with pipx.
I do have one small complaint about uv though. I want to be able to manage a project without adding a src directory, and without a README.md or hello.py file being forced on me. Mostly, I want to be able to teach this to beginners without having to explain any of these extra things…
For my own use (but not for teaching), I’ve found the following bash function helpful—I just put it into my .profile script:
# Quick `uv init` without src directory, "README.md", or "hello.py".# Project name is optional (default to directory if not provided).function uvi() {
if [ ! -z "$1" ] ; thenNAME="--name=$1"fi uv init --app --no-readme --no-workspace ${NAME} && rm hello.py
}
export -f uvi
With this, I can just type uvi and get a new project in my current directory without any of the extras. I just wish there was an easy way to run it in this mode for teaching purposes.