<?xml version="1.0" encoding="utf-8"?>
    <feed xmlns="http://www.w3.org/2005/Atom">
      <id>https://wolfgirl.dev/blog/</id>
      <title>PolyWolf's Blog - #programming</title>
      <subtitle
        >all posts tagged "programming" on a blog written by PolyWolf</subtitle
      >
      <icon>/apple-touch-icon.png</icon>
      <link href="/tags/programming/rss.xml" rel="self" />
      <link href="https://wolfgirl.dev/blog/" />
      <updated>2026-02-23T14:49:43Z</updated>
      
    <entry>
      <title><![CDATA[So I've Been Thinking About Static Site Generators]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2026-02-23-so-ive-been-thinking-about-static-site-generators/" />
      <id>https://wolfgirl.dev/blog/2026-02-23-so-ive-been-thinking-about-static-site-generators/</id>
      <published>2026-02-23T14:49:43Z</published>
      <updated>2026-02-23T14:49:43Z</updated>
      <summary><![CDATA[My current one is _fine_, but the rebuilds are a bit too long for my tastes (>10s) so I'd like to change that.]]></summary>
      <content type="html"><![CDATA[<p>I'm aware of 2 broad classes of SSGs: those written for the authors' personal use which are quirky in interesting ways (a <a href=https://maurycyz.com/misc/new_ssg/>very</a> <a href=https://gearsco.de/blog/blog-in-gleam/>common</a> <a href=https://aashvik.com/posts/shell-ssg/>topic</a> <a href=https://sukr.io/>on</a> lobste.rs), and ones written for a mass audience that are more mellow but flawed in some other way (<a href=https://jekyllrb.com/>Jekyll</a>, <a href=https://gohugo.io/>Hugo</a>, <a href=https://jaspervdj.be/hakyll/>Hakyll</a>, <a href=https://www.getzola.org/>Zola</a>, <a href=https://astro.build/>Astro</a>, & many others). I've dealt with the latter kind for as long as I've had a blog, so it's about time I took a crack at the former.</p><p>This post will go over what I want, which afaik nothing on the market satisfies, and what I plan on doing about that.</p><h2><a aria-hidden=true class=anchor href=#i-have-a-need-for-speed️ id=heading-i-have-a-need-for-speed️></a>I Have A Need... For Speed™️</h2><p>My top priorities in a static site generator are:</p><ol><li>I can port my current site to it without too much difficulty</li><li>It is very very fast, "🚀BLAZING🔥" even</li></ol><p>But what exactly does "fast" mean? Many times have I been saddened by Static Site Generators not being fast in certain ways, so learning from my experiences I'd like it to be fast in <strong>all possible ways</strong> please & thank you. Notably, we don't need a <a href=https://jade.fyi/blog/the-postmodern-build-system/>full-fledged build system</a> for this, so we'll cut corners wherever possible to focus on speed for the SSG niche.</p><h2><a aria-hidden=true class=anchor href=#1-i-want-fast-clean-builds id=heading-1-i-want-fast-clean-builds></a>(1) I Want Fast Clean Builds</h2><p>I'm not <em>too</em> concerned about these, because I would rather satisfy the upcoming requirements around re-builds, which are the majority of one's interaction with an SSG. 'd still be nice to be under a second tho.</p><p>Testing my current site, the most expensive part seems to be the in-memory transformations for all the separate files; surprisingly, file I/O itself is not the bottleneck! Here's an <a href=https://www.man7.org/linux/man-pages/man1/strace.1.html>strace</a>:</p><pre class=syntax-highlighting><code>$ strace -c node node_modules/astro/astro.js build
...
18:34:57 [build] 301 page(s) built in 12.34s
18:34:57 [build] Complete!
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ------------------
 23.41    0.091898           3     30254       472 futex
 12.57    0.049332           1     39546     14563 statx
  8.74    0.034312          25      1352           unlink
  6.76    0.026544           1     15055           read
  6.43    0.025225           0     28933     27981 readlink
  6.09    0.023891           1     12906      5956 openat
  4.63    0.018191           4      3955           write
  4.56    0.017918           2      6894           epoll_pwait
  4.19    0.016459           2      7551           close
  2.50    0.009800        2450         4           wait4
  2.31    0.009070           8      1045           io_uring_enter
  1.78    0.006971           7       953           mmap
  1.67    0.006570           6       969           munmap
  1.57    0.006181           3      1694           mprotect
  1.41    0.005520        1840         3           clone
  1.33    0.005221           0      5781      4622 access
  1.26    0.004962           4      1055           madvise
  1.24    0.004865           0      7471           getpid
  1.16    0.004536           7       609       301 rmdir
  1.14    0.004478           0      6084           setsockopt
  1.11    0.004349          13       327           brk
  1.08    0.004240           4      1029           fstat
...
</code></pre><p>The total reported build time is 12.34s, yet syscalls are only *does some quick math* 0.4s of that total! (Also, that number of syscalls seems totally unreasonable, but whatever.) My guess is having <em>everything</em> in Javascript accumulates performance woes from all the data shuffling, and there's not a whole lot to be done except change frameworks/languages.</p><h3><a aria-hidden=true class=anchor href=#-build-must-be-in-a-compiled-language id=heading--build-must-be-in-a-compiled-language></a>=> Build MUST Be In A Compiled Language</h3><p>This really is the only way we can make content transformations fast enough. Fortunately, there is a wealth of non-Javascript-based Markdown-To-HTML & HTML-To-Minified-HTML programs to choose from; <a href=https://github.com/kivikakk/comrak>comrak</a> & <a href=https://github.com/wilsonzlin/minify-html>minify-html</a> both look pretty good.</p><p>However... just because the "expensive" steps are in compiled languages, doesn't mean the build as a whole will be fast. Maybe I was holding it wrong, but trying a <a href=https://github.com/p0lyw0lf/website2/blob/74799c1253ad52f4e713169c6c35248931cadc1a/Makefile>Makefile-based solution</a> led to so many reads/process spawns that a clean build took about 5 seconds, already much too slow for my tastes. <code>strace</code> doesn't work quite as well here (besides saying "yes, waiting for subprocesses really is the blocker"), so I guess the cumulative overhead of running a bunch of processes that all touch a bunch of files (~600 of both) is just too much, somehow.</p><h3><a aria-hidden=true class=anchor href=#-build-should-be-a-single-process id=heading--build-should-be-a-single-process></a>=> Build SHOULD Be A Single Process</h3><p>Ideally, there's just one compiled program running the entire build start-to-finish, reading all the files into memory (my site is not very large), transforming them in-memory, and writing back exactly what's changed. This limits file I/O to the bare minimum, and we should be able to make the in-memory transforms fast too. Existing known-fast SSGs do this, so we must be on the right track!</p><h2><a aria-hidden=true class=anchor href=#2-i-want-fast-re-builds-when-content-changes id=heading-2-i-want-fast-re-builds-when-content-changes></a>(2) I Want Fast Re-Builds When Content Changes</h2><p>I <em>could</em> just say "Fast Re-Builds are just Fast Clean Builds" and be done with it pack it up go home; that's what Hugo, Zola, & most homebrew SSGs do. But no, it'd be boring doing the same thing as everyone else. We need super specil[sic] fast. We need something with a cache, a cache <em>so</em> robust & complete that a re-build runs <em>exactly</em> what's needed based on what's changed, and no more. This is known in the business as an "incremental build". My modest goal is sub-300ms rebuild times, ideally sub-100ms or even sub-20ms if I can swing it.</p><p>However! Turns out doing proper incremental builds is kinda tricky. I guess is this why most frameworks avoid it :P Even if you can swing complete dependency tracking, a naïve method of "this file is dirty so I should rebuild everything that depends on it" doesn't suffice, because that can and will over-rebuild. Consider the following scenario:</p><ul><li>Pages are set up such that each one has a link to the next and previous pages, if they exist.</li><li>Markdown files are stored with <a href=https://www.markdownlang.com/advanced/frontmatter.html>frontmatter</a> specifying the title that should be used for any links pointing to them.</li></ul><p>Then, the rebuild would go like this:</p><ol><li>Markdown file changed, marked dirty</li><li>=> Frontmatter metadata is marked dirty</li><li>=> Collection of <em>all</em> metadata is marked dirty</li><li>=> Any page reading from "collection of all metadata" is marked dirty</li><li>All pages read from "collection of all metadata" to create next/previous links</li><li>=> All pages are marked dirty</li></ol><p>Notably, step (2) happens even if only the page content changes! This means <em>any</em> change to <em>any</em> page causes <strong>the entire set of pages</strong> to get re-built, negating any benefits of caching.</p><p>You can somewhat get around this by breaking step (3), precisely tracking which pages read from what metadata so a more minimal set of pages are rebuilt, but an even better approach is to also break step (2), by <strong>only marking the frontmatter metadata as dirty if it actually changes.</strong></p><h3><a aria-hidden=true class=anchor href=#-build-must-use-a-real-incremental-algorithm id=heading--build-must-use-a-real-incremental-algorithm></a>=> Build MUST Use A Real Incremental Algorithm</h3><p>There are a couple approaches to this sort of fine-grained incrementality that I'm aware of:</p><ol><li>The Red-Green Algorithm <a href=https://salsa-rs.github.io/salsa/reference/algorithm.html#the-red-green-algorithm>as used by Rust for incremental compilation</a> & <a href=https://thunderseethe.dev/posts/lsp-base/#red-green-algorithm>explained really well by thunderseethe</a></li><li>Nix's derivation model, <a href=https://shuppy.org/posts/10000251.html>explained pretty well by shuppy</a></li><li>Jade's <a href=https://jade.fyi/blog/the-postmodern-build-system/>postmodern build system</a>, which also covers the first two</li><li><a href=https://www.microsoft.com/en-us/research/wp-content/uploads/2018/03/build-systems.pdf>Build Systems à la Carte</a>, which provides a framework for all possible other approaches</li></ol><p>I am a huge Rust-head so let's go with the first approach :3</p><p>How Red-Green deals with our example is, a step can be marked "green" (unchanged) even if it has "red" (changed) dependencies, so long as its output doesn't change on the re-run. This does the correct thing if both the metadata <em>hasn't</em> changed (only (1) is dirty, (2) is not), as well as if it <em>has</em> changed (1-3 are truly dirty, but many things at 4 aren't dirty because they read the unchanged parts).</p><details><summary>Here's a longer trace I wrote to convince myself this works</summary> <p>In our example, there are a few "queries" which are individually cached<sup class=footnote-ref><a data-footnote-ref href=#fn-queries id=fnref-queries>1</a></sup>:</p> <pre class=syntax-highlighting><code>fn parse_frontmatter(f: File) -> Metadata;
fn collect_metadata(fs: Vec[File]) -> CollectedMetadata;
fn index_metadata(cm: CollectedMetadata, i: usize) -> Option[Metadata];
fn build_page(f: File, cm: CollectedMetadata, i: usize) -> ();
fn build_all_pages(fs: Vec[File]) -> ();
</code></pre> <p>These queries form a dependency graph as follows:</p> <pre class=syntax-highlighting><code>build_all_pages() >---------------------\
       v                                |
       |                                |
       v                                |
collect_metadata()                      |
       v                                |
       |                                |
+------+--+---------+---------+         |
|         |         |         |         |
v         v         v         v         |
parse(0)  parse(1)  parse(2)  parse(3)  |
                                        |
index(0)  index(1)  index(2)  index(3)  |
^         ^         ^         ^         |
|         |         |         |         |
+--------/^\--------+--------/^         |
^\--------+--------/^\--------+         |
|         |         |         |         |
^         ^         ^         ^         |
build(0)  build(1)  build(2)  build(3)  |
^         ^         ^         ^         |
|         |         |         |         |
+---------+---------+---------+---------/
</code></pre> <p>Or, a bit more readably:</p> <ul><li><code>build_all_pages</code> depends on <code>collect_metadata</code> and all the individual <code>build</code> calls.</li><li>each <code>build</code> call depends on its own <code>index</code>, as well as the adjacent <code>index</code> calls.</li><li>each <code>index</code> call depends on "nothing", since it is pure wrt it's inputs. It still needs to be separate from the <code>parse</code> calls though, just in case file order changes during the collection step.</li><li>the <code>collect_metadata</code> call depends on each <code>parse</code> call.</li></ul> <p>Then, say we change the metadata for the first file. The algorithm works like[^rg-terminology]:</p> <ul><li><code>build_all_pages</code>: Have my dependencies changes? <ul><li><code>collect_metadata</code>: Have my dependencies changed? <ul><li><code>parse(0)</code>: Changed!</li></ul></li><li><code>collect_metadata</code>: Changed! Time to re-compute. <ul><li><code>parse(0)</code>: Already marked as changed</li><li><code>parse(1)</code>: Not changed</li><li><code>parse(2)</code>: Not changed</li><li><code>parse(3)</code>: Not changed</li></ul></li></ul></li><li><code>build_all_pages</code>: Changed! Time to re-compute. <ul><li><code>build(0)</code>: Have my dependencies changed? <ul><li><code>index(0)</code>: Changed!</li></ul></li><li><code>build(0)</code>: Changed! Time to re-compute. <ul><li><code>index(0)</code>: Already marked as changed.</li><li><code>index(1)</code>: Not changed</li></ul></li><li><code>build(1)</code>: Have my dependencies changed? <ul><li><code>index(0)</code>: Already marked as changed</li></ul></li><li><code>build(1)</code>: Changed! Time to re-compute <ul><li><code>index(0)</code>: Already marked as changed</li><li><code>index(1)</code>: Already marked as not changed</li><li><code>index(2)</code>: Not changed</li></ul></li><li><code>build(2)</code>: Have my dependencies changed? <ul><li><code>index(1)</code>: Already marked as not changed</li><li><code>index(2)</code>: Already marked as not changed</li><li><code>index(3)</code>: Not changed</li></ul></li><li><code>build(2)</code>: Not changed, no need to re-compute.</li><li>(<code>build(3)</code> is mostly the same as <code>build(2)</code>)</li></ul></li></ul> <p>As expected, we only needed to re-compute <code>build(0)</code> & <code>build(1)</code>. Neat!</p></details><p>All of this is also why we need hermetic (deterministic) builds. Red-Green & other incremental algorithms assume that "inputs didn't change" => "outputs won't change either". In the context of a general build system running external programs, this assumption may be incorrect or the inputs very hard to specify<sup class=footnote-ref><a data-footnote-ref href=#fn-hermeticity-hard id=fnref-hermeticity-hard>2</a></sup>. But in the context of a Static Site Generator where you can tightly control all the build logic, I think it is a perfectly fine assumption to make<sup class=footnote-ref><a data-footnote-ref href=#fn-tracing-easy id=fnref-tracing-easy>3</a></sup>.</p><h2><a aria-hidden=true class=anchor href=#3-i-want-fast-re-builds-when-logic-changes id=heading-3-i-want-fast-re-builds-when-logic-changes></a>(3) I Want Fast Re-Builds When Logic Changes</h2><p>Even among other SSGs that do have caching (which the vast majority do not, mind you), they'll assume "most the time you'll be editing content, requiring a full re-build when logic changes is totally fine." But that assumption is bad, actually. Having quick iteration times when hacking on the logic of the site is super valuable & not something I will give up. It's fine if those builds a little slower than changing mere content, but it still shouldn't require a clean build of the entire site just to change the number of posts shown on the home page.</p><p>This might be tricky, however, because the previous two requirements <em>reeeeeeally</em> want the build to be in a compiled language. But we'll get there. Just u watch.</p><p>First, I'd like to draw a distinction between the "build driver" (program that reads/transforms/writes files) and the "logic" (specification for <strong>what</strong> files are read/written, and what transforms will be done).</p><p>These don't have to be the same program! In fact, many, nay, most SSGs separate these concepts. A common models is a "build driver" (Jekyll, Hugo, Zola) that finds & interprets a bunch of template files containing site-specific logic. These frameworks vary in how much logic they let you express with those templates/configuration files, but what they share in common is <strong>the build driver stays the same for all runs</strong>.</p><p>My one gripe with this model is, the faster the builds are, the less powerful the templates tend to be. <a href=https://flower.jyn.dev/#why-clojure?>As jyn mentions</a>, writing logic inside such templating languages sucks & feels bad, so it'd be much nicer if we were able to write logic in a "real" language instead.</p><p>This brings us to another form of static site generator: one that merely provides a framework of composable tools to describe how your site should be built, and then "building the site" is just running the program you've assembled. Hakyll is an archetype for this, and I'd describe "powerful" Javascript frameworks such as Next.JS or Astro as this too: they all have "real languages" to push data into templates, rather than relying solely on folder structure/config files.</p><p>Unfortunately, this increased flexibility comes with a cost. If all the tools are Javascript, we get performance woes from earlier. And the logic is written in a compiled language, you have to re-compile whenever the logic changes, and that re-compile can take a while. Here are some language options we could choose, ordered by how familiar I am with it:</p><div class=table-wrapper><table><thead><tr><th align=left>Language</th><th align=left>Rebuilds</th><th align=left>Why Not?</th></tr></thead><tbody><tr><td align=left>Rust</td><td align=left>Incremental</td><td align=left>Still too slow (2-5s)</td></tr><tr><td align=left>Go</td><td align=left>Incremental</td><td align=left>I don't like it, only a bit faster than Rust</td></tr><tr><td align=left>C/C++</td><td align=left>Incremental</td><td align=left>hahahaha no</td></tr><tr><td align=left>Haskell</td><td align=left>Incremental</td><td align=left><em>Way</em> too slow, even with incremental</td></tr><tr><td align=left>Swift</td><td align=left>Incremental</td><td align=left>Incremental is overly-conservative, too slow</td></tr><tr><td align=left>OCaml</td><td align=left>Incremental</td><td align=left>Slow in many ways :)</td></tr><tr><td align=left>Zig</td><td align=left>Not Incremental</td><td align=left>Waiting for stable incremental support</td></tr><tr><td align=left>JVM-based</td><td align=left>?</td><td align=left>I don't know any, nor have a desire to</td></tr></tbody></table></div><p>The problem is, even among languages with incremental rebuilds, they still aren't "🚀BLAZING🔥FAST" enough for what I want. It is very impressive they're able to go as fast as they are, but remember I'm targeting sub-100ms, not sub-1s. That order of magnitude matters.</p><p>So, we've eliminated "just use templates", and also "just use a compiled language". That means we're left with:</p><h3><a aria-hidden=true class=anchor href=#-logic-must-be-in-a-scripting-language id=heading--logic-must-be-in-a-scripting-language></a>=> Logic MUST Be In A Scripting Language</h3><p>This will probably result in the speed tradeoff I am most satisfied with. Probably. "Doing the incremental steps" & "transforming the files" are still best done in a compiled language, because those are a thorny bit of programming with plenty of opportunity to be slow. The remaining logic can use that incrementality and be written in a scripting language to support faster on-the-fly changes. Separating the "build driver" from the "logic" is what other fast SSGs do after all, we're just making each a bit more powerful ;)</p><h2><a aria-hidden=true class=anchor href=#my-vehicle-of-choice-rust-binary--javascript-interpreter id=heading-my-vehicle-of-choice-rust-binary--javascript-interpreter></a>My Vehicle Of Choice: <a href=https://github.com/p0lyw0lf/driver>Rust Binary + Javascript Interpreter</a></h2><p>Based on everything above, I think you can see how we landed here: it's a hobby project, so I really really want to write Rust, and Javascript is a fine choice because:</p><ol><li>I know it well</li><li>The string templates are pretty good</li><li>It goes Fast Enough for logic</li><li>I'm very scared of homemade languages/Lisp</li></ol><p>Stuff that will go in the compiled Rust core:</p><ul><li>Incremental engine</li><li>Markdown parser (<a href=https://github.com/kivikakk/comrak>comrak</a>)</li><li>HTML minifier (<a href=https://github.com/wilsonzlin/minify-html>minify-html</a>)</li><li>URL Fetcher<sup class=footnote-ref><a data-footnote-ref href=#fn-fetching-urls id=fnref-fetching-urls>4</a></sup></li><li>Image Parser/Optimizer (TODO)</li><li>Javascript interpreter (<a href=https://github.com/delskayn/rquickjs>rquickjs</a>)</li></ul><p>And everything else, from base template partials to tag pages to the thing that makes a random PFP show up every time you refresh the page, will go in the Javascript files. It's another post entirely for how I'll actually structure the engine/scripts, so I'll leave it here for now :)</p><h2><a aria-hidden=true class=anchor href=#related-projects id=heading-related-projects></a>Related Projects</h2><p>Other incremental blog engines that inspired me to write this one:</p><ul><li>shuppy's cohost clone <a href=https://shuppy.org/posts/tagged/autost.html>autost</a></li><li>fasterthanlime's <a href=https://dodeca.bearcove.eu/>dodeca</a> project</li><li>jyn's <a href=https://flower.jyn.dev/>flower</a> project, especially their <a href=https://jyn.dev/four-posts-about-build-systems/>four posts about build systems</a></li></ul><p>I actually wanted to use one of these, but a hard look at each concluded they too fall under "for author's personal use only" and wouldn't suffice for my purposes. Still worth checking out tho!</p><p>Anyways that's it, stay tuned for if I ever finish this, hope you enjoyed ^^</p><section class=footnotes data-footnotes><ol><li id=fn-queries><p>A "real" set of queries might not look exactly like this (we don't necessarily want to pass down <code>CollectedMetadata</code> like that) but you get the overall idea. <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-queries>↩</a></p></li><li id=fn-hermeticity-hard><p>jyn's excellent post goes into <a href=https://jyn.dev/build-system-tradeoffs/#hermetic-builds>what exactly makes hermetic builds/tracing builds hard</a>, once again I highly recommend reading their series on build systems <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-hermeticity-hard>↩</a></p></li><li id=fn-tracing-easy><p>My plan is, effectively, a "tracing build system with a cached action graph". To once again respond to <a href=https://jyn.dev/build-system-tradeoffs/#tracing>jyn's build system tradeoffs post</a>: (1) I avoid the complexities of tracing system calls because all "system calls" are just functions exposed in Javascript, (2) I avoid the downsides of serializing my action graph because my website is not web-scale, (3) I avoid the "the same file can come from many possible places" a.k.a negative dependency problem by simply not allowing negative dependencies. Reading this tradeoff post was very useful, because it allowed me to end up with quite a compact design by discarding all the things I don't care about! <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-tracing-easy>↩</a></p></li><li id=fn-fetching-urls><p>I specifically want this to copy Astro's support for <a href=https://docs.astro.build/en/guides/images/#remote-images>remote images</a>, which makes things like the <code>&lt;Image /></code> component & <code>inferRemoteSize</code> Just Work. <a aria-label="Back to reference 4" class=footnote-backref data-footnote-backref data-footnote-backref-idx=4 href=#fnref-fetching-urls>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[I Improved My Blog Drafting Setup And So Can You]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2026-01-23-i-improved-my-blog-drafting-setup-and-so-can-you/" />
      <id>https://wolfgirl.dev/blog/2026-01-23-i-improved-my-blog-drafting-setup-and-so-can-you/</id>
      <published>2026-01-24T02:19:36Z</published>
      <updated>2026-01-24T02:19:36Z</updated>
      <summary><![CDATA[Recently I was on a team for MIT Mystery Hunt and we came in 8th place which was very fun. I crashed hard on Monday from lack of puzzle-s...]]></summary>
      <content type="html"><![CDATA[<p>Recently I was on a team for MIT Mystery Hunt and we came in 8th place which was very fun. I crashed hard on Monday from lack of puzzle-shaped stimulation, and then on Tuesday went "welp enough being bored" so now y'all get to see me shoot off to the right on the <a href=https://rakhim.org/images/honestly-undefined/blogging.jpg>"number of posts about elaborate blog setups" chart</a>. you understand yes.</p><p>So anyways ! I got around halfway thru another "blogging about blgging" post when I thought it might benefit from some feedback, forgoing of my typical "one & done" approach to drafting. The way I normally go about this is:</p><ol><li>I attach the raw Markdown file to a Discord message.</li><li>My recipients open it in the editor of their choice (usually <a href=https://obsidian.md/>Obsidian</a> because it's really good).</li></ol><p>But that sucks. I can't edit the attachments after-the-fact, so if I want to make a tweak, I'd either have to give everyone a diff to apply, or re-send the entire file to everyone, both of which are annoying for all parties involved. "Surely there must be a better way !"</p><p>Turns out anything is possible on the computer provided you apply enough force. So I that's what I did!</p><h2><a aria-hidden=true class=anchor href=#attempt-1-what-i-have-currently-but-better id=heading-attempt-1-what-i-have-currently-but-better></a>Attempt 1: What I Have Currently, But Better</h2><p>So. I already have a <a href=https://wolfgirl.dev/blog/2024-11-08-what-if-anyone-could-use-my-post-composer-/>post composer</a>. And it already saves drafts to <a href=https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage><code>localStorage</code></a>. What if I could make it save drafts to the server instead, and then the server could provide a read-only rendered view of those drafts to anyone who knows a given UUID?</p><p><a href=https://github.com/p0lyw0lf/crossposter/blob/d8c85c32a5b745c3148d2ab5099a85ffd8e56c93/rc/src/rc/drafts.py>Turns out that's not too hard at all</a>! It's a few more hacks on top of an already-creaky codebase, but it does in fact work. There are buttons for "save draft" (saves to remote server), "edit draft" (loads from remote server), & "delete draft" (deletes on remote server), and an up-to-date lists of drafts is fetched on every page load.</p><p>However, this got me thinking: All the server is doing is managing markdown files, exactly how I'm working locally. Yet, despite being "just files" under the hood, the way I interacted with those files through the web interface was very... special. I still had to manually copy from my Obsidian vault where the "true" drafts lived, and the web-based file interface was much more clunky than anything I had natively. "Surely there must be a better way !"</p><h2><a aria-hidden=true class=anchor href=#attempt-2-re-thinking-the-interaction id=heading-attempt-2-re-thinking-the-interaction></a>Attempt 2: Re-Thinking The Interaction</h2><p>Of course there is. <strong>Anything</strong> is possible on the computer; I simply needed to provide more force. By the power of my three remaining brain cells colliding all at once, I came up with an Ideal Workflow:</p><ul><li>I can edit the files in Obsidian, locally, where I've always been editing them.</li><li>Said files near-instantly show up behind a URL, assuming I have internet to upload with.</li></ul><p>From there, it's just a matter of figuring out how to enable this :)</p><p>I already have <a href=https://syncthing.net/>Syncthing</a> set up for syncing drafts between my phone and my desktop, so I also added it to be synced to the <code>/var/opt/syncthing</code> directory on the server the post composer lives on, using Docker because I'm lazy (<a href=https://github.com/syncthing/syncthing/blob/cb391d25b8825140d029ef13b8dc9a6bc9b6209d/README-Docker.md>docs</a>):</p><pre class=syntax-highlighting><code># syncthing/docker-compose.yml
services:
  syncthing:
    image: syncthing/syncthing
    container_name: syncthing
    hostname: syncthing
    environment:
      - PUID=1001
      - PGID=1001
      - STGUIADDRESS=
    volumes:
      - /var/opt/syncthing:/var/syncthing
    network_mode: host
    restart: unless-stopped
    healthcheck:
      test: curl -fkLsS -m 2 127.0.0.1:8384/rest/noauth/health | grep -o --color=never OK || exit 1
      interval: 1m
      timeout: 10s
      retries: 3
</code></pre><pre class=syntax-highlighting><code># systemd/syncthing.service
[Unit]
Description=Syncthing via Docker Compose
Requires=docker.service
After=docker.service

[Service]
Restart=always
User=root
Group=docker
TimeoutStopSec=15
WorkingDirectory=/home/ubuntu/infrastructure/syncthing
ExecStartPre=/usr/bin/docker compose -f docker-compose.yml down
ExecStart=/usr/bin/docker compose -f docker-compose.yml up
ExecStop=/usr/bin/docker compose -f docker-compose.yml down

[Install]
WantedBy=multi-user.target
</code></pre><p>and, of course, we also want to expose the management interface remotely, just in case things go wrong (<a href=https://docs.syncthing.net/users/reverseproxy.html>docs</a>):</p><pre class=syntax-highlighting><code># nginx/rc.wolfgirl.dev.nginx
server {
	# ...
	location /syncthing/ {
		# Authenticate with ngx_http_auth_request_module, for my custom JWT authentication
		auth_request	/auth;

		# Needed in order for syncthing to know it's secure behind a reverse proxy
		proxy_set_header	Host "localhost";
		proxy_set_header	X-Real-IP $remote_addr;
		proxy_set_header	X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header	X-Forwarded-Proto $scheme;

		proxy_pass	http://localhost:8384/;

		proxy_read_timeout	600s;
		proxy_send_timeout	600s;
	}	
	# ...
}
</code></pre><p>Then, I can configure my website to read from a <em>subdirectory</em> of that synced folder. (It's important to be a subdirectory, since that means I can change the visibility of a draft at any time.) Finally, I can re-use the post renderer I made in Attempt 1, get rid of most the composer functionality, add a simple file explorer, and blam! Drafts :)</p><p>At this point, one might think "well why don't I just move my entire site onto this system?", but I have good reason not to: the finished post site has a very different performance profile compared to a drafts site. Because finished posts (1) change much less often and (2) serve much more traffic, I prefer to serve it from statically-generated files in an S3 bucket behind AWS Cloudfront w/ generous cache policies, whereas the drafts site is perfectly good to "re-render Markdown to HTML every time a request is made" on a VPS w/ no caching. There are tradeoffs with either approach, and the usecase requirements inform each deployment.</p><p>This does mean the markdown rendering/CSS for drafts vs finished posts are slightly different. Overall it's not a huge biggie, but at some point I would like to unify them. That is a fair bit harder tho, so I'll leave that for later :)</p><p>Thanks for reading, hope this inspires some more cursed setups!! :3</p>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[4 Unconventional Ways to Cast in Typescript]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2025-10-22-4-unconventional-ways-to-cast-in-typescript/" />
      <id>https://wolfgirl.dev/blog/2025-10-22-4-unconventional-ways-to-cast-in-typescript/</id>
      <published>2025-10-23T02:43:18Z</published>
      <updated>2025-10-23T02:43:18Z</updated>
      <summary><![CDATA[I saw a post by qntm and remembered I had a playground with a similar idea. I then expanded that playground into a (probably non-exhausti...]]></summary>
      <content type="html"><![CDATA[<p>I saw a <a href=https://bsky.app/profile/qntm.org/post/3m3dak7s63k2v>post by qntm</a> and remembered I had a playground with a similar idea. I then expanded that playground into a (probably non-exhaustive) list of ways to cast between arbitrary<sup class=footnote-ref><a data-footnote-ref href=#fn-arbitrary id=fnref-arbitrary>1</a></sup> types in Typescript:</p><h2><a aria-hidden=true class=anchor href=#convention-the-as-operator id=heading-convention-the-as-operator></a>Convention: The <code>as</code> Operator</h2><p>Ah, good ol' <code>as</code>:</p><pre class=syntax-highlighting><code class=language-ts><span class=keyword>const</span> <span class=function>cast</span> <span class=operator>=</span> <span class=operator>&lt;</span><span class=type>A</span><span class=punctuation>,</span> <span class=type>B</span><span class=punctuation>,</span><span class=operator>></span><span class=punctuation>(</span><span class=variable>a</span>: <span class=type>A</span><span class=punctuation>)</span>: <span class=type>B</span> <span class=operator>=></span> <span class=variable>a</span> <span class=keyword>as</span> <span class=type>unknown</span> <span class=keyword>as</span> <span class=type>B</span><span class=punctuation>;</span></code></pre><p>We can't just directly do <code>a as B</code> because Typescript is smart enough to warn us about <strong>that</strong>, at least. That very same error message also says,</p><blockquote><p>If this was intentional, convert the expression to 'unknown' first.</p></blockquote><p>So we can just do that :3</p><p>If we were approaching this from a type theoretic perspective, it's already done & dusted, we have the most cut-and-dry demonstration of unsoundness, pack it up go home.</p><p>But, what if we couldn't use <code>as</code>? Can we still get between two completely unrelated types?</p><h2><a aria-hidden=true class=anchor href=#unconvention-1-the-is-operator id=heading-unconvention-1-the-is-operator></a>Unconvention 1: The <code>is</code> Operator</h2><p><code>is</code> is commonly used for for interfacing with Typescript's flow-typing system, helping it figure out what exactly the return value of a boolean function <em>means</em>. For example:</p><pre class=syntax-highlighting><code class=language-ts><span class=keyword>const</span> <span class=function>notUndefined1</span> <span class=operator>=</span> <span class=operator>&lt;</span><span class=type>A</span><span class=punctuation>,</span><span class=operator>></span><span class=punctuation>(</span><span class=variable>a</span>: <span class=type>A</span> <span class=operator>|</span> <span class=constant>undefined</span><span class=punctuation>)</span>: <span class=type>boolean</span> <span class=operator>=></span> <span class=variable>a</span> <span class=operator>!==</span> <span class=constant>undefined</span><span class=punctuation>;</span>
<span class=keyword>const</span> <span class=function>notUndefined2</span> <span class=operator>=</span> <span class=operator>&lt;</span><span class=type>A</span><span class=punctuation>,</span><span class=operator>></span><span class=punctuation>(</span><span class=variable>a</span>: <span class=type>A</span> <span class=operator>|</span> <span class=constant>undefined</span><span class=punctuation>)</span>: <span class=variable>a</span> is <span class=type>A</span> <span class=operator>=></span> <span class=variable>a</span> <span class=operator>!==</span> <span class=constant>undefined</span><span class=punctuation>;</span>

<span class=keyword>const</span> <span class=variable>maybeNumber0</span>: <span class=type>number</span> <span class=operator>|</span> <span class=constant>undefined</span> <span class=operator>=</span> <span class=function>someExternalFunction</span><span class=punctuation>();</span>
<span class=keyword>if</span> <span class=punctuation>(</span><span class=variable>maybeNumber0</span> <span class=operator>!==</span> <span class=constant>undefined</span><span class=punctuation>)</span> <span class=keyword>return</span><span class=punctuation>;</span>
<span class=comment>// Thanks to flow-typing, Typescript knows that `maybeNumber0: number`</span>
<span class=comment>// if we get here.</span>
<span class=keyword>const</span> <span class=variable>maybeNumber1</span> <span class=operator>=</span> <span class=function>someExternalFunction</span><span class=punctuation>();</span>
<span class=keyword>if</span> <span class=punctuation>(</span><span class=function>notUndefined1</span><span class=punctuation>(</span><span class=variable>maybeNumber1</span><span class=punctuation>))</span> <span class=keyword>return</span><span class=punctuation>;</span>
<span class=comment>// However, Typescript cannot infer flow from ordinary functions;</span>
<span class=comment>// At this point, it still thinks `maybeNumber1: number | undefined`</span>
<span class=keyword>const</span> <span class=variable>maybeNumber2</span> <span class=operator>=</span> <span class=function>someExternalFunction</span><span class=punctuation>();</span>
<span class=keyword>if</span> <span class=punctuation>(</span><span class=function>notUndefined2</span><span class=punctuation>(</span><span class=variable>maybeNumber2</span><span class=punctuation>))</span> <span class=keyword>return</span><span class=punctuation>;</span>
<span class=comment>// The `is` annotation has the exact same `boolean` value at runtime,</span>
<span class=comment>// but provides extra information to the compiler, so Typescript can know</span>
<span class=comment>// that `maybeNumber2: number` if we get here.</span></code></pre><p>However, <code>is</code> is sort of an escape hatch outside the regular typing system, and we can abuse it to tell the compiler whatever we want:</p><pre class=syntax-highlighting><code class=language-ts><span class=keyword>const</span> <span class=function>badDetector</span> <span class=operator>=</span> <span class=operator>&lt;</span><span class=type>A</span><span class=punctuation>,</span> <span class=type>B</span><span class=punctuation>,</span><span class=operator>></span><span class=punctuation>(</span><span class=variable>a</span>: <span class=type>A</span><span class=punctuation>)</span>: <span class=type>B</span> <span class=operator>=></span> <span class=punctuation>{</span>
    <span class=keyword>const</span> <span class=function>detector</span> <span class=operator>=</span> <span class=punctuation>(</span><span class=variable>_ab</span>: <span class=type>A</span> <span class=operator>|</span> <span class=type>B</span><span class=punctuation>)</span>: <span class=variable>_ab</span> is <span class=type>B</span> <span class=operator>=></span> <span class=constant>true</span><span class=punctuation>;</span>
    <span class=keyword>if</span> <span class=punctuation>(</span><span class=function>detector</span><span class=punctuation>(</span><span class=variable>a</span><span class=punctuation>))</span> <span class=keyword>return</span> <span class=variable>a</span><span class=punctuation>;</span>
    <span class=keyword>throw</span> <span class=keyword>new</span> <span class=type>Error</span><span class=punctuation>(</span><span class=string>"unreachable"</span><span class=punctuation>);</span>
<span class=punctuation>};</span></code></pre><p>Typescript doesn't (and can't!) check that the function body is actually doing what the <code>is</code> assertion says. So we can just write a bad one on purpose! (Or on accident, introducing quite a subtle bug.)</p><h2><a aria-hidden=true class=anchor href=#unconvention-2-mutation-across-boundaries id=heading-unconvention-2-mutation-across-boundaries></a>Unconvention 2: Mutation Across Boundaries</h2><p>This cast requires a "seed" value <code>b: B</code> in order to be able to cast <code>a: A</code> to <code>B</code>, but make no mistake: this sort of thing can come up fairly often if we're not careful about how we mutate objects.</p><pre class=syntax-highlighting><code class=language-ts><span class=keyword>const</span> <span class=function>mutation</span> <span class=operator>=</span> <span class=operator>&lt;</span><span class=type>A</span><span class=punctuation>,</span> <span class=type>B</span><span class=punctuation>,</span><span class=operator>></span><span class=punctuation>(</span><span class=variable>a</span>: <span class=type>A</span><span class=punctuation>,</span> <span class=variable>b</span>: <span class=type>B</span><span class=punctuation>)</span>: <span class=type>B</span> <span class=operator>=></span> <span class=punctuation>{</span>
    <span class=keyword>const</span> <span class=function>mutate</span> <span class=operator>=</span> <span class=punctuation>(</span><span class=variable>obj</span>: <span class=punctuation>{</span> <span class=property>field</span>: <span class=type>A</span> <span class=operator>|</span> <span class=type>B</span> <span class=punctuation>})</span>: <span class=type>void</span> <span class=operator>=></span> <span class=punctuation>{</span>
        <span class=variable>obj</span><span class=punctuation>.</span><span class=property>field</span> <span class=operator>=</span> <span class=variable>a</span><span class=punctuation>;</span>
    <span class=punctuation>};</span>

    <span class=keyword>const</span> <span class=variable>obj</span> <span class=operator>=</span> <span class=punctuation>{</span> <span class=property>field</span>: <span class=variable>b</span> <span class=punctuation>};</span>
    <span class=function>mutate</span><span class=punctuation>(</span><span class=variable>obj</span><span class=punctuation>);</span>
    <span class=keyword>return</span> <span class=variable>obj</span><span class=punctuation>.</span><span class=property>field</span><span class=punctuation>;</span>
<span class=punctuation>};</span></code></pre><p>I showed this to a type theory friend and their reaction was:</p><blockquote><p>bruh ts type system mega fails Variance is hard xd xd xd</p></blockquote><p>What they meant by that was, the coercion from <code>{ field: B }</code> to <code>{ field: A | B }</code> is unsafe when the destination <code>field</code> is mutable; if we allow it, we get exactly the behavior shown here. To make it safe we'd need <code>{ readonly field: A | B }</code>, which then prevents the mutation.</p><p>Another way of thinking about this is, Typescript currently has no way to "flow" the cast of/assignment to <code>obj.field</code> after the function runs. (Potentially on purpose, because that would make the type system even more complex & limit certain useful patterns.) Inlining the <code>obj.field = a;</code> allows us to catch this, but the analysis does not go across function boundaries.</p><h2><a aria-hidden=true class=anchor href=#unconvention-3-smuggling-through-structural-typing id=heading-unconvention-3-smuggling-through-structural-typing></a>Unconvention 3: Smuggling Through Structural Typing</h2><p>Typescript is structurally typed. This means that, if we have an <code>obj: { field: string }</code>, all we know is that there exists an <code>obj.field: string</code>. Typescript doesn't care at all if <code>obj</code> has other fields, and in fact this is the biggest advantage of structural typing: we can freely "upcast" to less restrictive types (i.e. fewer fields) without having to change runtime representations.</p><p>The downside of this sort of upcasting is that, some operations like <code>Object.values</code>/the spread operator are only properly typed when they have a complete list of fields, and have their assumptions violated when extra fields are in the mix:</p><pre class=syntax-highlighting><code class=language-ts><span class=keyword>const</span> <span class=function>loopSmuggling</span> <span class=operator>=</span> <span class=operator>&lt;</span><span class=type>A</span><span class=punctuation>,</span> <span class=type>B</span><span class=punctuation>,</span><span class=operator>></span><span class=punctuation>(</span><span class=variable>a</span>: <span class=type>A</span><span class=punctuation>,</span> <span class=variable>b</span>: <span class=type>B</span><span class=punctuation>)</span>: <span class=type>B</span> <span class=operator>=></span> <span class=punctuation>{</span>
    <span class=keyword>const</span> <span class=variable>objAB</span> <span class=operator>=</span> <span class=punctuation>{</span> <span class=property>fieldA</span>: <span class=variable>a</span><span class=punctuation>,</span> <span class=property>fieldB</span>: <span class=variable>b</span> <span class=punctuation>};</span>
    <span class=keyword>const</span> <span class=variable>objB</span>: <span class=punctuation>{</span> <span class=property>fieldB</span>: <span class=type>B</span> <span class=punctuation>}</span> <span class=operator>=</span> <span class=variable>objAB</span><span class=punctuation>;</span>
    <span class=keyword>for</span> <span class=punctuation>(</span><span class=keyword>const</span> <span class=variable>field</span> <span class=keyword>of</span> <span class=type>Object</span><span class=punctuation>.</span><span class=function>values</span><span class=punctuation>(</span><span class=variable>objB</span><span class=punctuation>))</span> <span class=punctuation>{</span>
        <span class=comment>// Object.values believes all fields have type `B`,</span>
        <span class=comment>// but actually `fieldA` is first in iteration order.</span>
        <span class=keyword>return</span> <span class=variable>field</span><span class=punctuation>;</span>
    <span class=punctuation>}</span>
    <span class=keyword>throw</span> <span class=keyword>new</span> <span class=type>Error</span><span class=punctuation>(</span><span class=string>"unreachable"</span><span class=punctuation>);</span>
<span class=punctuation>};</span>

<span class=keyword>const</span> <span class=function>spreadSmuggling</span> <span class=operator>=</span> <span class=operator>&lt;</span><span class=type>A</span><span class=punctuation>,</span> <span class=type>B</span><span class=punctuation>,</span><span class=operator>></span><span class=punctuation>(</span><span class=variable>a</span>: <span class=type>A</span><span class=punctuation>,</span> <span class=variable>b</span>: <span class=type>B</span><span class=punctuation>)</span>: <span class=type>B</span> <span class=operator>=></span> <span class=punctuation>{</span>
    <span class=keyword>const</span> <span class=variable>objA</span> <span class=operator>=</span> <span class=punctuation>{</span> <span class=property>field</span>: <span class=variable>a</span> <span class=punctuation>};</span>
    <span class=keyword>const</span> <span class=variable>obj</span>: <span class=punctuation>{}</span> <span class=operator>=</span> <span class=variable>objA</span><span class=punctuation>;</span>
    <span class=keyword>const</span> <span class=variable>objB</span> <span class=operator>=</span> <span class=punctuation>{</span> <span class=property>field</span>: <span class=variable>b</span><span class=punctuation>,</span> ...<span class=variable>obj</span> <span class=punctuation>};</span>
    <span class=comment>// `objB.field` has been overwritten by the spread,</span>
    <span class=comment>// but Typescript doesn't know that.</span>
    <span class=keyword>return</span> <span class=variable>objB</span><span class=punctuation>.</span><span class=property>field</span><span class=punctuation>;</span>
<span class=punctuation>};</span></code></pre><p>These casts have the same restriction as (2), in that we require a "seed" value <code>b: B</code> in order to make it typecheck. Still, it's a bit of a double-whammy, because trying to avoid (2) by copying objects with a <code>...</code> spread can make you run smack-dab into this one elsewhere.</p><h2><a aria-hidden=true class=anchor href=#unconvention-4--void-is-very-bad id=heading-unconvention-4--void-is-very-bad></a>Unconvention 4: <code>| void</code> is Very Bad</h2><p>This one's by far the most unconventional; the rest you're probably aware of if you've worked with Typescript for a while, but this one hardly ever comes up because it's such a "why even do this" kinda deal. Still, I have seen it in my work's codebase (and immediately excised it once I realized), so it's not <em>impossible</em> to come across.</p><p>Anyways here it is:</p><pre class=syntax-highlighting><code class=language-ts><span class=keyword>const</span> <span class=function>orVoid</span> <span class=operator>=</span> <span class=operator>&lt;</span><span class=type>A</span><span class=punctuation>,</span> <span class=type>B</span><span class=punctuation>,</span><span class=operator>></span><span class=punctuation>(</span><span class=variable>a</span>: <span class=type>A</span><span class=punctuation>)</span>: <span class=type>B</span> <span class=operator>=></span> <span class=punctuation>{</span>
    <span class=keyword>const</span> <span class=function>outer</span> <span class=operator>=</span> <span class=punctuation>(</span><span class=variable>inner</span>: <span class=punctuation>()</span> <span class=operator>=></span> <span class=type>B</span> <span class=operator>|</span> <span class=type>void</span><span class=punctuation>)</span>: <span class=type>B</span> <span class=operator>=></span> <span class=punctuation>{</span>
        <span class=keyword>const</span> <span class=variable>b</span> <span class=operator>=</span> <span class=function>inner</span><span class=punctuation>();</span>
        <span class=keyword>if</span> <span class=punctuation>(</span><span class=variable>b</span><span class=punctuation>)</span> <span class=keyword>return</span> <span class=variable>b</span><span class=punctuation>;</span>
        <span class=keyword>throw</span> <span class=keyword>new</span> <span class=type>Error</span><span class=punctuation>(</span><span class=string>"falsy"</span><span class=punctuation>);</span>
    <span class=punctuation>};</span>

    <span class=keyword>const</span> <span class=function>returnsA</span> <span class=operator>=</span> <span class=punctuation>()</span>: <span class=type>A</span> <span class=operator>=></span> <span class=variable>a</span><span class=punctuation>;</span>
    <span class=keyword>const</span> <span class=variable>voidSmuggled</span>: <span class=punctuation>()</span> <span class=operator>=></span> <span class=type>void</span> <span class=operator>=</span> <span class=variable>returnsA</span><span class=punctuation>;</span>
    <span class=keyword>return</span> <span class=function>outer</span><span class=punctuation>(</span><span class=variable>voidSmuggled</span><span class=punctuation>);</span>
<span class=punctuation>};</span></code></pre><p>This is a combination of a few interesting things. For Typescript, <code>void</code> is primarily seen as a function's return value, indicating "I don't care what this function returns because I'm not going to use it". This is why any function, including our <code>() => A</code> one, can be safely coerced to <code>() => void</code>. Usually, this is safe, because once we have a <code>() => void</code>, we really can't assign its output to a variable, nor can we directly type a value as <code>void</code>; it's a very special type after all.</p><p>However, <code>void</code> can still participate in type combinations like <code>B | void</code>. And, because functions are <a href=https://www.typescriptlang.org/docs/handbook/2/generics.html#variance-annotations>covariant</a> in their return type, <code>() => void</code> can be safely coerced to <code>() => B | void</code>. And, as it turns out, we <em>can</em> assign that <code>B | void</code> return type to a variable!</p><p>If <code>void</code> were meant to be assigned directly, it <em>should</em> behave something more like <code>any</code> or <code>unknown</code>. But it's not, so instead it behaves like a <a href=https://developer.mozilla.org/en-US/docs/Glossary/Falsy>falsy</a> type, because a normal <code>void</code>-returning function actually returns <code>undefined</code> at runtime. This is how we're able to <code>if (b) return b;</code> (which is <strong>not</strong> the same as checking <code>b</code>'s true type!) & still have everything typecheck.</p><p>Unfortunately, that means this cast only works for truthy <code>a</code>. But that's not too much of an issue I think, the Cool Factor outweighs this limitation :3</p><h2><a aria-hidden=true class=anchor href=#does-this-even-matter id=heading-does-this-even-matter></a>Does This Even Matter?</h2><p>Yes, but it's complicated.</p><p>On the one hand, Typescript is clearly just a "best effort" at adding types to Javascript, and it does a darn good job at that. If you're holding it right, these things don't come up, and your code genuinely is much much safer than if you used raw Javascript.</p><p>On the other hand, all these "unconventions" are real footguns one can stumble into & unintentionally introduce unsafety into your codebase. It only takes a little bit of unsoundness in one place to render entire swaths buggy. We can do our best to avoid these patterns manually, but an automated solution will always be better at catching them.</p><h2><a aria-hidden=true class=anchor href=#what-can-we-do-about-this id=heading-what-can-we-do-about-this></a>What Can We Do About This?</h2><p><strong>TL;DR</strong> use <a href=https://typescript-eslint.io/>typescript-eslint</a><sup class=footnote-ref><a data-footnote-ref href=#fn-typescript-eslint id=fnref-typescript-eslint>2</a></sup>. While neither Typescript<sup class=footnote-ref><a data-footnote-ref href=#fn-typescript id=fnref-typescript>3</a></sup> nor Eslint<sup class=footnote-ref><a data-footnote-ref href=#fn-eslint id=fnref-eslint>4</a></sup> on their own come with enough rules to detect any of these, the typescript-eslint ruleset includes things like <a href=https://typescript-eslint.io/rules/prefer-readonly-parameter-types/>@typescript-eslint/prefer-readonly-parameter-types</a> (prevents (2)), <a href=https://typescript-eslint.io/rules/no-invalid-void-type/>@typescript-eslint/no-invalid-void-type</a> (prevents (4)), & <a href=https://typescript-eslint.io/rules/no-unnecessary-type-parameters/>@typescript-eslint/no-unnecessary-type-parameters</a> (prevents the rest by making the <code>unknown</code> viral). Unfortunately, all of them are opt-in, and Typescript + eslint + typescript-eslint always requires a fair bit of mucking about to get working.</p><p>Anyways, I hope these examples are enough to convince you to use more aggressive linting on your Typescript projects in the future :3</p><section class=footnotes data-footnotes><ol><li id=fn-arbitrary><p>OK, I may have over-exaggerated on the "arbitrary" part a little :P (1) and (2) do in fact work for everything, but (3) doesn't work for <code>undefined</code> sometimes and (4) only works if the <code>a</code> to be cast is truthy. Still, it's a good enough demonstration I think. <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-arbitrary>↩</a></p></li><li id=fn-typescript-eslint><p><a href="https://typescript-eslint.io/play/#ts=5.9.2&fileType=.tsx&code=PQKhAIGEHsDsDcCmsAuBLO4TAFAGM4BnFcPAQ2PAF5wAeAQQBpwAhRgPgAoyAucegJR8W1duDLjC4AK6wA1rGgB3WJNYBuHDlAQAqrAIJk6TAEY%2BAFQAWicAAM0hO%2BADyAB0QAnMimiesuIaUAEZkACYAIogoiHi%2B-jQMzGxcvPxCrKLgAN444PmkRCRh0bHx1OCcAPpkwXz04AA%2BrBk1weCOmVRiKJ7SiJoFHQBmlSUxcX7cAgLgntHSnqpkgwUoVp7K4LCISuAAop6bnpwARLLzZHhWtQA2iKcCmgC%2Bmtpg4PqGSKgYqgBMfAAstIUD4-vw8JtCFIWNBZGEyJ40IgpNh8EVwABbUHgzCJJisDjcerMOotYRZXJDIIkHFgmIVTjQYIAKz42XAwxRtzC9SamWeGXg0DQYSpeSGBRZrIAdNzELyKitJflXloaZiZRVsgreXxguqhvSfIhmWynqq5gsluAZfKeWEXm8dJ8DHAfiZVABmWXmcAAZRxAHNg7c0LBg%2BBrJtpMGrIHetI4osyLdowBPNwRqPo2ngW7QaBuINxsM5ipJImpUngcksDIibo5K35mX0Js5LmO%2Bh8MjMPVhFgG8BGgpttnDruDqciZ4VdssVb5YZ%2BSr5wd20YuNllWXwNP9Qjm1kN2bUqX5YDAVy7uL7w%2BouuKlFIKRp9ODqQ3JDgFBZ2w7BYOxGCtIZrzrUFxBTD8M3sQd6GcTpuU8SgIw6GJvC9O1PBKTxZTAgp5hQRZVEHZdRytdZNj2HY9kOY4zguRArhuYJ7keZ0tFdL4PWMCFfUBQMQ3LSNow2eF40TPoU28dMLCzCs80xQg3EuMJS1DcMxIJZJiTSQl60bCVNVgSh2x1btFT5cRRwoid2RyecaHbeytUnSzBwNZhZV87UxyvG87BlFgHWs5wbikYJEGQO0kE8JRkRQGJVGCOD1lsVT1NA8Cb2CKCFI8QgoTQNximgVFYAAchIBQtnWHwCKGYjSLtScwt5Lj3j0d0jF%2BTAABY%2BGcZoRTFJCpAANS8OCWHCAIMTMkg-Em0VxV06sSXSSlmwvfI21BLwmQjHZPD4ThZmbERRrW4zdsI-bMXaGgTq8C6KKGNBRk4YJZha21gg%2BtYJNo3YDiOKZTmGNNCAzTirXVVtMX%2BsyGhoC7%2BWbFVTMoMaNJE%2B4bIurI8YqFHCHoCiUbtQ6TjxzSw0QMJLXVIA&eslintrc=N4KABGBEBOCuA2BTAzpAXGUEKQAIBcBPABxQGNoBLY-AWhXkoDt8B6AQwBMArdsxFrQD2AN0TR4QrrWSUA5k3b5Y0FOijjoQ6JAA04bHiKlkFanQbM27aNHaFaxxOsibtegzgIlyVGvWRGFg4Ad3ZKOnwACwF2ACMkFzcdfWwobxMzf0tguPYmR2RaMiEAW1KBfCTbd1TDDN9zAKC2PIL8QKtisoqWaq0Uz3SnUz8LTuCyeHZkIsZ8cXZ4WmItUmgiGSJEjFcawbSjH1GmnLYpmaKK6KFOIthkREcoylRd5I9DhpPsifOhJiyZALQRyATiShkbqA-BwMj4bRvDT7T71EZZcYtVglQGvEF0ZicRAAD0QnGEcW4iHhW0IO2RA1RXnRY2aVmxAKB%2BNoqmU0CY-VqQyOmVZZw5uOBlUcPlol3E%2BEonMFBzRxwxbMmnLx0qctCJADNmBElYCVUzhuqxX8JVzdbKScRtB1zXVmVbTjacXbBHrKKUnRskXtGW7LaLPVjDewEHRiDZ2KVaNNga7hd8NeLOEI6Ewc0pTWmvizI%2BzHYwyBFaAbYEx4aaeYg%2Be0fEW1RHfljy5CqxVSnFxHKyPxZpQ4pR5oQ2%2B6O5iy8TiBXe7cEE84kJa5wbA4RtPw41O%2BzjXQiRc7Irle8UWGRQe58FSuxiSsE6Vgx8bxnrVi%2BwPoMJoCJKgmDkPdbx%2Be82GuKJbhkeRFD5J5gTpZwr1DdMS0PYJFFKZg5GhMQWELNChWLD0sLYPM5VsexoWBOEER0EjVRnO9NUooRqLsBwiSQBYwK-UtsM4vJHkcTj6LwgTMMg1gqJxGtZBA2g8wKJgEGWeUNmIhlSPbNjxXkgFFLw2gRCEShyUdVRRwBaTyNkqiiVWaklDJezZ3YuTOM4WBSGKFMrkQftxHfa8MIcrynL8is3PodSkxEJZYDUZiLXAzMbWixdITivVvUVZRKjC9CyM8wyfMIHDIX1RA%2BNQ3SWP3CCos44KaAcGs6wvAU0s-GTWvoANNiscQDT4BqQz01iWoqoaOopKkaScDyDKytqFyXOh8inPqIvK9b6GJWF2BUgEVI0uVZgVHSpqajLv3ZKiSROphEA3IoLlmVbZsOg1JALZTVjKV5Usa9LBIo7zq20WhmC4%2BwfsyrEqP9HKyXoJL4CRx7hLhgNnRlfzZCJegDQNakXT2sq1pRzjmApmiEieXdqf0366bhpgscs55XhxoSOK5nnyXM3mVrZmbkaezjJCEYhq1rMgBahqjJFmYQDRWVRK1kOzJea6W8cfOQaoSv8Sumw3caFip8jwpANbF8l5cWRiVcczjcOQB4MbekIPcG73ffJYHvbBu6IYGubg8eclkBcrhA5jyhSSshLLfuyHPZUxMUHjfhk8O1SLvgTTro2P2NNeKJuiWcgpINh7Behkv1LLq7Hkrl2aFNJZiiicJevB-rIrmtvLq0nqi851RT2mVQZ5lxtfKYLdfVlAqIhSlhM6jsfDtUABHWBKFUfHAypkf9tp5ebNhSEFnJNHnT30eDtnlAH-hDHWevmmObL2QIPbMAcm7Z0GtEV4cpGAzCXnjPUSxKBwPAdHQ6tY3ojmQNuWg64hBIHyMmCIixlglADDYSaH4b6ALxhg6kKBsHQAcDiTgJp9b-3ZkbIWdCsE4PjHYa4g5gbrE2JceCvQqioIPpzHhDCcEnyQUacQ8DuFMEwXIphjh2rTAWEdFytlh6R3frfWhaj6GzBwYg6AchYASLftQrh0NZEWM0Ygiu08pEf2Xs4xhO5N6chOlYFRTizG8Ncf47moVbpUIAY4qiPjLGyn4XnBY0B7GxJtiE7BFNqI2IkcE%2BJgJ2A5LEQofJniTGqOyU8MgSxsYVJoVU4pTx54Jh6rQCo1jG4cKlpkwp1T4q2O6OQqgyB2FGIcX0zitYBldXrOdCWPTrYt36c0jpwU-xDiwQU6ZRScm8hUIYmJnCpm0BmWstxXcPFLObqrXZAzaw4NwupdJJyVm7JDnomyeszQNLiR8uOZkbCvN6e8s5YkBwGm0C0xARo3o7PBXVBhdFYSwHhO4P5pzfaOyKO1TYjpnQIqSv%2BY%2Bp8bIIpCHYYg6xFqUyJhHY5oK7lqQ0vEJA9LO43XOshekEyMktwBPAHcUQtAhHoCiTFLdkmCP-MIm6DLwr8qhi5RmV0UXBJVYOIk9E0V8m6Xyt5yrVCqoEEM48yDGAAC9QoauNYOOFnBbWwvtbDIQBonWqrme0xZBqmWyU1f%2BZgUxYDao9YOeYJDBlJl-MoyVRrnX-hwvnCatAADWiBCAhG0I6uN-q7WJursAuujtKwgTDf%2BeWPV%2B5kEHswct2sQZiVUEtCw%2BwQXLPjaq1QXBBW7RuRA8UAbGw9qYEKl8AimyDj-r6jteaE0rzRSzJJr5J1MX7WgrEQ7VBggXEdak9bJLKWBDYDo8U7i0BCBEKI9aDn8j5kUH1jLZ1eSHaex0dKPi5q8qoHRGMwT4FSTIJsgH4xn3bbcvNjanheobDMKqysv3ilJWfJ4NhuIyEJmQ-hlDFWGtksh8%2B7AwgRGCffKgNJFwPGEOsfIdxSNf3I5EbRcVrIMNNOBgdNpb0FCI%2BESR67pHsjGRsDlW8iq72CZJGkeCCEFFYwYjjG6hOXvwDWo6g8HiKkIsimt1JU3BIfouJCKZa75oEIXRDNonCGmCbWdcm51k3BzQJrxwRayUCUfHeCSgVAKtKnhryvszlMFTXmEIBR4a1NU7XWpZc8hkFTUCqgbKcOMgMAAXxAOloAA&tsconfig=N4KABGBEDGD2C2AHAlgGwKYCcDyiAuysAdgM6QBcYoEEkJemy0eAcgK6qoDCAFutAGsylBm3TgwAXxCSgA&tokens=false">typescript-eslint playground</a> <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-typescript-eslint>↩</a></p></li><li id=fn-typescript><p><a href=https://www.typescriptlang.org/play/?#code/PQKhAIGEHsDsDcCmsAuBLO4TAFAGM4BnFcPAQ2PAF5wAeAQQBpwAhRgPgAoyAucegJR8W1duDLjC4AK6wA1rGgB3WJNYBuHDlAQAqrAIJk6TAEY+AFQAWicAAM0hO+ADyAB0QAnMimiesuIaUAEZkACYAIogoiHi+-jQMzGxcvPxCrKLgAN44AJBBJGHRsfHU4JwA+mTBfPTgAD6sGdXB4I6ZVGIontKImnloAGYVxTFxftwCAuCe0dKeqmQDKFaeyuCwiErgAKKe656cAESyc2R4VjUANojHApoAvpraYOD6hkioGKoATHwAWWkKB8P34eHWhCkLGgsjCZE8aEQUmw+CIJAAtsDQZhEkxWBxuHVmLVmsIsrkCujwFiQTFypxoMEAFZ8bLgIZI65hOqNTKPGZdHL5PJM5kAOk5iG55WW+WeWipsEoYvK7Kl3L4bQVeVpPkQjJZD3ycxQC1UYslXLCTy0OneBjgXxMqgAzOLzOAAMpYgDmvuuaFgvvA1nW0l9Vm9PWkcQWZGuoYAnm4gyHUYVwNdoNA3D6IwG0+UkgTUsTwKSWBkRELKZmxfQazkOdb6HwyMwNWEWFrwDr6yye82u0ORI9yg2WAMhn4Kpmu+BoCMXCzSuL4Am+oRDcyqzNKXlgMBXKu4uvN8iK9KkUgpAnE12pFckOAUCnbHYWHZGCKjxXgeIcb3km9hdvQzgdJyniUEG7QxN4LqLp4xSeOKIqmuaLbSja8o4OA+GvmsGxbDs+yHCcZyIBcVzBLc9y2q8eiOkY3yYO6-zen6hbBqGRERlGXoxnG3iJhYKZFhm1KEG45xhPm-qBjxeLJISaT4pW1YUvkA7MvUNDqta7Z9gMOlsuONANiZ1Jik2BnYVqzDik5qo6n+dg2Va2HOFcUjBIgyCLkgnhKIiKAxKowQgastjSbJP6HsewQAWJHiEBCaBuEU0DIrAADkJAKBsqw+GheQYYsi6Dp53IMfaHxOsYYIACx8M4TTwNAaBhBBUgAGpeCBLDhAEaLKiQfi9Z1YTFviKREuk5K1tp1nAl4DJBlsnh8JwgpiCI7VTZpS15EqITlBtXg7QMgwjJwwQzOVEXXas6w7CRewHJMxxDAmhBJvRuHLWNszzIshB6RUu3iFZwMdV18kBogPKQ1kcPTTQj3gwMj2LqtRxowjtxhMajxAA>typescript playground</a> <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-typescript>↩</a></p></li><li id=fn-eslint><p><a href="https://eslint.org/play/#eyJ0ZXh0IjoiLy8gRGlzYWJsaW5nIGEgYnVuY2ggb2YgdGhpbmdzIHRoYXQgaGF2ZSBubyBpbXBhY3Qgb24gdHlwaW5nXG4vKiBlc2xpbnQgbm8tdW51c2VkLXZhcnM6IDAgKi9cbi8qIGVzbGludCBpZC1sZW5ndGg6IDAgKi9cbi8qIGVzbGludCBvbmUtdmFyOiAwICovXG4vKiBlc2xpbnQgb2JqZWN0LXByb3BlcnR5LW5ld2xpbmU6IDAgKi9cbi8qIGVzbGludCBwYWRkZWQtYmxvY2tzOiAwICovXG4vKiBlc2xpbnQgbmV3bGluZS1hZnRlci12YXI6IDAgKi9cbi8qIGVzbGludCBuZXdsaW5lLWJlZm9yZS1yZXR1cm46IDAgKi9cbi8qIGVzbGludCBjb21tYS1zcGFjaW5nOiAwICovXG4vKiBlc2xpbnQgY3VybHk6IDAgKi9cbi8qIGVzbGludCBxdW90ZS1wcm9wczogMCAqL1xuLyogZXNsaW50IG9iamVjdC1jdXJseS1zcGFjaW5nOiAwICovXG4vKiBlc2xpbnQgbXVsdGlsaW5lLWNvbW1lbnQtc3R5bGU6ICovXG4vKiBlc2xpbnQgY2FwaXRhbGl6ZWQtY29tbWVudHM6IDAgKi9cblxuLyoqIENvbnZlbnRpb24gKi9cbmNvbnN0IGNhc3QgPSA8QSwgQiw+KGE6IEEpOiBCID0+IGEgYXMgdW5rbm93biBhcyBCO1xuXG4vKiogVW5jb252ZW50aW9uIDE6IFRoZSBgaXNgIE9wZXJhdG9yICovXG5jb25zdCBiYWREZXRlY3RvciA9IDxBLCBCLD4oYTogQSk6IEIgPT4ge1xuICAgIGNvbnN0IGRldGVjdG9yID0gKF9hYjogQSB8IEIpOiBfYWIgaXMgQiA9PiB0cnVlO1xuICAgIGlmIChkZXRlY3RvcihhKSkgcmV0dXJuIGE7XG4gICAgdGhyb3cgbmV3IEVycm9yKFwidW5yZWFjaGFibGVcIik7XG59O1xuXG4vKiogVW5jb252ZW50aW9uIDI6IE11dGF0aW9uIEFjcm9zcyBCb3VuZGFyaWVzICovXG5jb25zdCBtdXRhdGlvbiA9IDxBLCBCLD4oYTogQSwgYjogQik6IEIgPT4ge1xuICAgIGNvbnN0IG11dGF0ZSA9IChvYmo6IHsgZmllbGQ6IEEgfCBCIH0pID0+IHtcbiAgICAgICAgb2JqLmZpZWxkID0gYTtcbiAgICB9O1xuXG4gICAgY29uc3Qgb2JqID0ge2ZpZWxkOiBifTtcbiAgICBtdXRhdGUob2JqKTtcbiAgICByZXR1cm4gb2JqLmZpZWxkO1xufTtcblxuLyoqIFVuY29udmVudGlvbiAzLjE6IFNtdWdnbGluZyBUaHJvdWdoIFN0cnVjdHVyYWwgVHlwaW5nICovXG5jb25zdCBsb29wU211Z2dsaW5nID0gPEEsIEIsPihhOiBBLCBiOiBCKTogQiA9PiB7XG4gICAgY29uc3Qgb2JqQUIgPSB7IGZpZWxkQTogYSwgZmllbGRCOiBiIH07XG4gICAgY29uc3Qgb2JqQjogeyBmaWVsZEI6IEIgfSA9IG9iakFCO1xuICAgIGZvciAoY29uc3QgZmllbGQgb2YgT2JqZWN0LnZhbHVlcyhvYmpCKSkge1xuICAgICAgICAvLyBPYmplY3QudmFsdWVzIGJlbGlldmVzIGFsbCBmaWVsZHMgaGF2ZSB0eXBlIGBCYCxcbiAgICAgICAgLy8gYnV0IGFjdHVhbGx5IGBmaWVsZEFgIGlzIGZpcnN0IGluIGl0ZXJhdGlvbiBvcmRlci5cbiAgICAgICAgcmV0dXJuIGZpZWxkO1xuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoXCJ1bnJlYWNoYWJsZVwiKTtcbn07XG5cbi8qKiBVbmNvbnZlbnRpb24gMy4yOiBTbXVnZ2xpbmcgVGhyb3VnaCBTdHJ1Y3R1cmFsIFR5cGluZyAqL1xuY29uc3Qgc3ByZWFkU211Z2dsaW5nID0gPEEsIEIsPihhOiBBLCBiOiBCKTogQiA9PiB7XG4gICAgY29uc3Qgb2JqQSA9IHsgZmllbGQ6IGEgfTtcbiAgICBjb25zdCBvYmo6IHt9ID0gb2JqQTtcbiAgICBjb25zdCBvYmpCID0geyBmaWVsZDogYiwgLi4ub2JqIH07XG4gICAgLy8gYG9iakIuZmllbGRgIGhhcyBiZWVuIG92ZXJ3cml0dGVuIGJ5IHRoZSBzcHJlYWQsXG4gICAgLy8gYnV0IFR5cGVzY3JpcHQgZG9lc24ndCBrbm93IHRoYXQuXG4gICAgcmV0dXJuIG9iakIuZmllbGQ7XG59O1xuXG4vKiogVW5jb252ZW50aW9uIDQ6IGAgfCB2b2lkYCBpcyBWZXJ5IEJhZCAqL1xuY29uc3Qgb3JWb2lkID0gPEEsIEIsPihhOiBBKTogQiA9PiB7XG4gICAgY29uc3Qgb3V0ZXIgPSAoaW5uZXI6ICgpID0+IEIgfCB2b2lkKTogQiA9PiB7XG4gICAgICAgIGNvbnN0IGIgPSBpbm5lcigpO1xuICAgICAgICBpZiAoYikgcmV0dXJuIGI7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcImZhbHN5XCIpO1xuICAgIH07XG5cbiAgICBjb25zdCByZXR1cm5zQSA9ICgpID0+IGE7XG4gICAgY29uc3Qgdm9pZFNtdWdnbGVkOiAoKSA9PiB2b2lkID0gcmV0dXJuc0E7XG4gICAgcmV0dXJuIG91dGVyKHZvaWRTbXVnZ2xlZCk7XG59O1xuIiwib3B0aW9ucyI6eyJydWxlcyI6eyJhY2Nlc3Nvci1wYWlycyI6WyJlcnJvciJdLCJhcnJheS1icmFja2V0LW5ld2xpbmUiOlsiZXJyb3IiXSwiYXJyYXktYnJhY2tldC1zcGFjaW5nIjpbImVycm9yIl0sImFycmF5LWNhbGxiYWNrLXJldHVybiI6WyJlcnJvciJdLCJhcnJheS1lbGVtZW50LW5ld2xpbmUiOlsiZXJyb3IiXSwiYXJyb3ctYm9keS1zdHlsZSI6WyJlcnJvciJdLCJhcnJvdy1wYXJlbnMiOlsiZXJyb3IiXSwiYXJyb3ctc3BhY2luZyI6WyJlcnJvciJdLCJibG9jay1zY29wZWQtdmFyIjpbImVycm9yIl0sImJsb2NrLXNwYWNpbmciOlsiZXJyb3IiXSwiYnJhY2Utc3R5bGUiOlsiZXJyb3IiXSwiY2FsbGJhY2stcmV0dXJuIjpbImVycm9yIl0sImNhbWVsY2FzZSI6WyJlcnJvciJdLCJjYXBpdGFsaXplZC1jb21tZW50cyI6WyJlcnJvciJdLCJjbGFzcy1tZXRob2RzLXVzZS10aGlzIjpbImVycm9yIl0sImNvbW1hLWRhbmdsZSI6WyJlcnJvciJdLCJjb21tYS1zcGFjaW5nIjpbImVycm9yIl0sImNvbW1hLXN0eWxlIjpbImVycm9yIl0sImNvbXBsZXhpdHkiOlsiZXJyb3IiXSwiY29tcHV0ZWQtcHJvcGVydHktc3BhY2luZyI6WyJlcnJvciJdLCJjb25zaXN0ZW50LXJldHVybiI6WyJlcnJvciJdLCJjb25zaXN0ZW50LXRoaXMiOlsiZXJyb3IiXSwiY29uc3RydWN0b3Itc3VwZXIiOlsiZXJyb3IiXSwiY3VybHkiOlsiZXJyb3IiXSwiZGVmYXVsdC1jYXNlIjpbImVycm9yIl0sImRlZmF1bHQtY2FzZS1sYXN0IjpbImVycm9yIl0sImRlZmF1bHQtcGFyYW0tbGFzdCI6WyJlcnJvciJdLCJkb3QtbG9jYXRpb24iOlsiZXJyb3IiXSwiZG90LW5vdGF0aW9uIjpbImVycm9yIl0sImVvbC1sYXN0IjpbImVycm9yIl0sImVxZXFlcSI6WyJlcnJvciJdLCJmb3ItZGlyZWN0aW9uIjpbImVycm9yIl0sImZ1bmMtY2FsbC1zcGFjaW5nIjpbImVycm9yIl0sImZ1bmMtbmFtZS1tYXRjaGluZyI6WyJlcnJvciJdLCJmdW5jLW5hbWVzIjpbImVycm9yIl0sImZ1bmMtc3R5bGUiOlsiZXJyb3IiXSwiZnVuY3Rpb24tY2FsbC1hcmd1bWVudC1uZXdsaW5lIjpbImVycm9yIl0sImZ1bmN0aW9uLXBhcmVuLW5ld2xpbmUiOlsiZXJyb3IiXSwiZ2VuZXJhdG9yLXN0YXItc3BhY2luZyI6WyJlcnJvciJdLCJnZXR0ZXItcmV0dXJuIjpbImVycm9yIl0sImdsb2JhbC1yZXF1aXJlIjpbImVycm9yIl0sImdyb3VwZWQtYWNjZXNzb3ItcGFpcnMiOlsiZXJyb3IiXSwiZ3VhcmQtZm9yLWluIjpbImVycm9yIl0sImhhbmRsZS1jYWxsYmFjay1lcnIiOlsiZXJyb3IiXSwiaWQtYmxhY2tsaXN0IjpbImVycm9yIl0sImlkLWRlbnlsaXN0IjpbImVycm9yIl0sImlkLWxlbmd0aCI6WyJlcnJvciJdLCJpZC1tYXRjaCI6WyJlcnJvciJdLCJpbXBsaWNpdC1hcnJvdy1saW5lYnJlYWsiOlsiZXJyb3IiXSwiaW5kZW50IjpbImVycm9yIl0sImluZGVudC1sZWdhY3kiOlsiZXJyb3IiXSwiaW5pdC1kZWNsYXJhdGlvbnMiOlsiZXJyb3IiXSwianN4LXF1b3RlcyI6WyJlcnJvciJdLCJrZXktc3BhY2luZyI6WyJlcnJvciJdLCJrZXl3b3JkLXNwYWNpbmciOlsiZXJyb3IiXSwibGluZS1jb21tZW50LXBvc2l0aW9uIjpbImVycm9yIl0sImxpbmVicmVhay1zdHlsZSI6WyJlcnJvciJdLCJsaW5lcy1hcm91bmQtY29tbWVudCI6WyJlcnJvciJdLCJsaW5lcy1hcm91bmQtZGlyZWN0aXZlIjpbImVycm9yIl0sImxpbmVzLWJldHdlZW4tY2xhc3MtbWVtYmVycyI6WyJlcnJvciJdLCJsb2dpY2FsLWFzc2lnbm1lbnQtb3BlcmF0b3JzIjpbImVycm9yIl0sIm1heC1jbGFzc2VzLXBlci1maWxlIjpbImVycm9yIl0sIm1heC1kZXB0aCI6WyJlcnJvciJdLCJtYXgtbGVuIjpbImVycm9yIl0sIm1heC1saW5lcyI6WyJlcnJvciJdLCJtYXgtbGluZXMtcGVyLWZ1bmN0aW9uIjpbImVycm9yIl0sIm1heC1uZXN0ZWQtY2FsbGJhY2tzIjpbImVycm9yIl0sIm1heC1wYXJhbXMiOlsiZXJyb3IiXSwibWF4LXN0YXRlbWVudHMiOlsiZXJyb3IiXSwibWF4LXN0YXRlbWVudHMtcGVyLWxpbmUiOlsiZXJyb3IiXSwibXVsdGlsaW5lLWNvbW1lbnQtc3R5bGUiOlsiZXJyb3IiXSwibXVsdGlsaW5lLXRlcm5hcnkiOlsiZXJyb3IiXSwibmV3LWNhcCI6WyJlcnJvciJdLCJuZXctcGFyZW5zIjpbImVycm9yIl0sIm5ld2xpbmUtYWZ0ZXItdmFyIjpbImVycm9yIl0sIm5ld2xpbmUtYmVmb3JlLXJldHVybiI6WyJlcnJvciJdLCJuZXdsaW5lLXBlci1jaGFpbmVkLWNhbGwiOlsiZXJyb3IiXSwibm8tYWxlcnQiOlsiZXJyb3IiXSwibm8tYXJyYXktY29uc3RydWN0b3IiOlsiZXJyb3IiXSwibm8tYXN5bmMtcHJvbWlzZS1leGVjdXRvciI6WyJlcnJvciJdLCJuby1hd2FpdC1pbi1sb29wIjpbImVycm9yIl0sIm5vLWJpdHdpc2UiOlsiZXJyb3IiXSwibm8tYnVmZmVyLWNvbnN0cnVjdG9yIjpbImVycm9yIl0sIm5vLWNhbGxlciI6WyJlcnJvciJdLCJuby1jYXNlLWRlY2xhcmF0aW9ucyI6WyJlcnJvciJdLCJuby1jYXRjaC1zaGFkb3ciOlsiZXJyb3IiXSwibm8tY2xhc3MtYXNzaWduIjpbImVycm9yIl0sIm5vLWNvbXBhcmUtbmVnLXplcm8iOlsiZXJyb3IiXSwibm8tY29uZC1hc3NpZ24iOlsiZXJyb3IiXSwibm8tY29uZnVzaW5nLWFycm93IjpbImVycm9yIl0sIm5vLWNvbnNvbGUiOlsiZXJyb3IiXSwibm8tY29uc3QtYXNzaWduIjpbImVycm9yIl0sIm5vLWNvbnN0YW50LWJpbmFyeS1leHByZXNzaW9uIjpbImVycm9yIl0sIm5vLWNvbnN0YW50LWNvbmRpdGlvbiI6WyJlcnJvciJdLCJuby1jb25zdHJ1Y3Rvci1yZXR1cm4iOlsiZXJyb3IiXSwibm8tY29udGludWUiOlsiZXJyb3IiXSwibm8tY29udHJvbC1yZWdleCI6WyJlcnJvciJdLCJuby1kZWJ1Z2dlciI6WyJlcnJvciJdLCJuby1kZWxldGUtdmFyIjpbImVycm9yIl0sIm5vLWRpdi1yZWdleCI6WyJlcnJvciJdLCJuby1kdXBlLWFyZ3MiOlsiZXJyb3IiXSwibm8tZHVwZS1jbGFzcy1tZW1iZXJzIjpbImVycm9yIl0sIm5vLWR1cGUtZWxzZS1pZiI6WyJlcnJvciJdLCJuby1kdXBlLWtleXMiOlsiZXJyb3IiXSwibm8tZHVwbGljYXRlLWNhc2UiOlsiZXJyb3IiXSwibm8tZHVwbGljYXRlLWltcG9ydHMiOlsiZXJyb3IiXSwibm8tZWxzZS1yZXR1cm4iOlsiZXJyb3IiXSwibm8tZW1wdHkiOlsiZXJyb3IiXSwibm8tZW1wdHktY2hhcmFjdGVyLWNsYXNzIjpbImVycm9yIl0sIm5vLWVtcHR5LWZ1bmN0aW9uIjpbImVycm9yIl0sIm5vLWVtcHR5LXBhdHRlcm4iOlsiZXJyb3IiXSwibm8tZW1wdHktc3RhdGljLWJsb2NrIjpbImVycm9yIl0sIm5vLWVxLW51bGwiOlsiZXJyb3IiXSwibm8tZXZhbCI6WyJlcnJvciJdLCJuby1leC1hc3NpZ24iOlsiZXJyb3IiXSwibm8tZXh0ZW5kLW5hdGl2ZSI6WyJlcnJvciJdLCJuby1leHRyYS1iaW5kIjpbImVycm9yIl0sIm5vLWV4dHJhLWJvb2xlYW4tY2FzdCI6WyJlcnJvciJdLCJuby1leHRyYS1sYWJlbCI6WyJlcnJvciJdLCJuby1leHRyYS1wYXJlbnMiOlsiZXJyb3IiXSwibm8tZXh0cmEtc2VtaSI6WyJlcnJvciJdLCJuby1mYWxsdGhyb3VnaCI6WyJlcnJvciJdLCJuby1mbG9hdGluZy1kZWNpbWFsIjpbImVycm9yIl0sIm5vLWZ1bmMtYXNzaWduIjpbImVycm9yIl0sIm5vLWdsb2JhbC1hc3NpZ24iOlsiZXJyb3IiXSwibm8taW1wbGljaXQtY29lcmNpb24iOlsiZXJyb3IiXSwibm8taW1wbGljaXQtZ2xvYmFscyI6WyJlcnJvciJdLCJuby1pbXBsaWVkLWV2YWwiOlsiZXJyb3IiXSwibm8taW1wb3J0LWFzc2lnbiI6WyJlcnJvciJdLCJuby1pbmxpbmUtY29tbWVudHMiOlsiZXJyb3IiXSwibm8taW5uZXItZGVjbGFyYXRpb25zIjpbImVycm9yIl0sIm5vLWludmFsaWQtcmVnZXhwIjpbImVycm9yIl0sIm5vLWludmFsaWQtdGhpcyI6WyJlcnJvciJdLCJuby1pcnJlZ3VsYXItd2hpdGVzcGFjZSI6WyJlcnJvciJdLCJuby1pdGVyYXRvciI6WyJlcnJvciJdLCJuby1sYWJlbC12YXIiOlsiZXJyb3IiXSwibm8tbGFiZWxzIjpbImVycm9yIl0sIm5vLWxvbmUtYmxvY2tzIjpbImVycm9yIl0sIm5vLWxvbmVseS1pZiI6WyJlcnJvciJdLCJuby1sb29wLWZ1bmMiOlsiZXJyb3IiXSwibm8tbG9zcy1vZi1wcmVjaXNpb24iOlsiZXJyb3IiXSwibm8tbWFnaWMtbnVtYmVycyI6WyJlcnJvciJdLCJuby1taXNsZWFkaW5nLWNoYXJhY3Rlci1jbGFzcyI6WyJlcnJvciJdLCJuby1taXhlZC1vcGVyYXRvcnMiOlsiZXJyb3IiXSwibm8tbWl4ZWQtcmVxdWlyZXMiOlsiZXJyb3IiXSwibm8tbWl4ZWQtc3BhY2VzLWFuZC10YWJzIjpbImVycm9yIl0sIm5vLW11bHRpLWFzc2lnbiI6WyJlcnJvciJdLCJuby1tdWx0aS1zcGFjZXMiOlsiZXJyb3IiXSwibm8tbXVsdGktc3RyIjpbImVycm9yIl0sIm5vLW11bHRpcGxlLWVtcHR5LWxpbmVzIjpbImVycm9yIl0sIm5vLW5hdGl2ZS1yZWFzc2lnbiI6WyJlcnJvciJdLCJuby1uZWdhdGVkLWNvbmRpdGlvbiI6WyJlcnJvciJdLCJuby1uZWdhdGVkLWluLWxocyI6WyJlcnJvciJdLCJuby1uZXN0ZWQtdGVybmFyeSI6WyJlcnJvciJdLCJuby1uZXciOlsiZXJyb3IiXSwibm8tbmV3LWZ1bmMiOlsiZXJyb3IiXSwibm8tbmV3LW5hdGl2ZS1ub25jb25zdHJ1Y3RvciI6WyJlcnJvciJdLCJuby1uZXctb2JqZWN0IjpbImVycm9yIl0sIm5vLW5ldy1yZXF1aXJlIjpbImVycm9yIl0sIm5vLW5ldy1zeW1ib2wiOlsiZXJyb3IiXSwibm8tbmV3LXdyYXBwZXJzIjpbImVycm9yIl0sIm5vLW5vbm9jdGFsLWRlY2ltYWwtZXNjYXBlIjpbImVycm9yIl0sIm5vLW9iai1jYWxscyI6WyJlcnJvciJdLCJuby1vYmplY3QtY29uc3RydWN0b3IiOlsiZXJyb3IiXSwibm8tb2N0YWwiOlsiZXJyb3IiXSwibm8tb2N0YWwtZXNjYXBlIjpbImVycm9yIl0sIm5vLXBhcmFtLXJlYXNzaWduIjpbImVycm9yIl0sIm5vLXBhdGgtY29uY2F0IjpbImVycm9yIl0sIm5vLXBsdXNwbHVzIjpbImVycm9yIl0sIm5vLXByb2Nlc3MtZW52IjpbImVycm9yIl0sIm5vLXByb2Nlc3MtZXhpdCI6WyJlcnJvciJdLCJuby1wcm9taXNlLWV4ZWN1dG9yLXJldHVybiI6WyJlcnJvciJdLCJuby1wcm90byI6WyJlcnJvciJdLCJuby1wcm90b3R5cGUtYnVpbHRpbnMiOlsiZXJyb3IiXSwibm8tcmVkZWNsYXJlIjpbImVycm9yIl0sIm5vLXJlZ2V4LXNwYWNlcyI6WyJlcnJvciJdLCJuby1yZXN0cmljdGVkLWV4cG9ydHMiOlsiZXJyb3IiXSwibm8tcmVzdHJpY3RlZC1nbG9iYWxzIjpbImVycm9yIl0sIm5vLXJlc3RyaWN0ZWQtaW1wb3J0cyI6WyJlcnJvciJdLCJuby1yZXN0cmljdGVkLW1vZHVsZXMiOlsiZXJyb3IiXSwibm8tcmVzdHJpY3RlZC1wcm9wZXJ0aWVzIjpbImVycm9yIl0sIm5vLXJlc3RyaWN0ZWQtc3ludGF4IjpbImVycm9yIl0sIm5vLXJldHVybi1hc3NpZ24iOlsiZXJyb3IiXSwibm8tcmV0dXJuLWF3YWl0IjpbImVycm9yIl0sIm5vLXNjcmlwdC11cmwiOlsiZXJyb3IiXSwibm8tc2VsZi1hc3NpZ24iOlsiZXJyb3IiXSwibm8tc2VsZi1jb21wYXJlIjpbImVycm9yIl0sIm5vLXNlcXVlbmNlcyI6WyJlcnJvciJdLCJuby1zZXR0ZXItcmV0dXJuIjpbImVycm9yIl0sIm5vLXNoYWRvdyI6WyJlcnJvciJdLCJuby1zaGFkb3ctcmVzdHJpY3RlZC1uYW1lcyI6WyJlcnJvciJdLCJuby1zcGFjZWQtZnVuYyI6WyJlcnJvciJdLCJuby1zcGFyc2UtYXJyYXlzIjpbImVycm9yIl0sIm5vLXN5bmMiOlsiZXJyb3IiXSwibm8tdGFicyI6WyJlcnJvciJdLCJuby10ZW1wbGF0ZS1jdXJseS1pbi1zdHJpbmciOlsiZXJyb3IiXSwibm8tdGVybmFyeSI6WyJlcnJvciJdLCJuby10aGlzLWJlZm9yZS1zdXBlciI6WyJlcnJvciJdLCJuby10aHJvdy1saXRlcmFsIjpbImVycm9yIl0sIm5vLXRyYWlsaW5nLXNwYWNlcyI6WyJlcnJvciJdLCJuby11bmFzc2lnbmVkLXZhcnMiOlsiZXJyb3IiXSwibm8tdW5kZWYiOlsiZXJyb3IiXSwibm8tdW5kZWYtaW5pdCI6WyJlcnJvciJdLCJuby11bmRlZmluZWQiOlsiZXJyb3IiXSwibm8tdW5kZXJzY29yZS1kYW5nbGUiOlsiZXJyb3IiXSwibm8tdW5leHBlY3RlZC1tdWx0aWxpbmUiOlsiZXJyb3IiXSwibm8tdW5tb2RpZmllZC1sb29wLWNvbmRpdGlvbiI6WyJlcnJvciJdLCJuby11bm5lZWRlZC10ZXJuYXJ5IjpbImVycm9yIl0sIm5vLXVucmVhY2hhYmxlIjpbImVycm9yIl0sIm5vLXVucmVhY2hhYmxlLWxvb3AiOlsiZXJyb3IiXSwibm8tdW5zYWZlLWZpbmFsbHkiOlsiZXJyb3IiXSwibm8tdW5zYWZlLW5lZ2F0aW9uIjpbImVycm9yIl0sIm5vLXVuc2FmZS1vcHRpb25hbC1jaGFpbmluZyI6WyJlcnJvciJdLCJuby11bnVzZWQtZXhwcmVzc2lvbnMiOlsiZXJyb3IiXSwibm8tdW51c2VkLWxhYmVscyI6WyJlcnJvciJdLCJuby11bnVzZWQtcHJpdmF0ZS1jbGFzcy1tZW1iZXJzIjpbImVycm9yIl0sIm5vLXVudXNlZC12YXJzIjpbImVycm9yIl0sIm5vLXVzZS1iZWZvcmUtZGVmaW5lIjpbImVycm9yIl0sIm5vLXVzZWxlc3MtYXNzaWdubWVudCI6WyJlcnJvciJdLCJuby11c2VsZXNzLWJhY2tyZWZlcmVuY2UiOlsiZXJyb3IiXSwibm8tdXNlbGVzcy1jYWxsIjpbImVycm9yIl0sIm5vLXVzZWxlc3MtY2F0Y2giOlsiZXJyb3IiXSwibm8tdXNlbGVzcy1jb21wdXRlZC1rZXkiOlsiZXJyb3IiXSwibm8tdXNlbGVzcy1jb25jYXQiOlsiZXJyb3IiXSwibm8tdXNlbGVzcy1jb25zdHJ1Y3RvciI6WyJlcnJvciJdLCJuby11c2VsZXNzLWVzY2FwZSI6WyJlcnJvciJdLCJuby11c2VsZXNzLXJlbmFtZSI6WyJlcnJvciJdLCJuby11c2VsZXNzLXJldHVybiI6WyJlcnJvciJdLCJuby12YXIiOlsiZXJyb3IiXSwibm8tdm9pZCI6WyJlcnJvciJdLCJuby13YXJuaW5nLWNvbW1lbnRzIjpbImVycm9yIl0sIm5vLXdoaXRlc3BhY2UtYmVmb3JlLXByb3BlcnR5IjpbImVycm9yIl0sIm5vLXdpdGgiOlsiZXJyb3IiXSwibm9uYmxvY2stc3RhdGVtZW50LWJvZHktcG9zaXRpb24iOlsiZXJyb3IiXSwib2JqZWN0LWN1cmx5LW5ld2xpbmUiOlsiZXJyb3IiXSwib2JqZWN0LWN1cmx5LXNwYWNpbmciOlsiZXJyb3IiXSwib2JqZWN0LXByb3BlcnR5LW5ld2xpbmUiOlsiZXJyb3IiXSwib2JqZWN0LXNob3J0aGFuZCI6WyJlcnJvciJdLCJvbmUtdmFyIjpbImVycm9yIl0sIm9uZS12YXItZGVjbGFyYXRpb24tcGVyLWxpbmUiOlsiZXJyb3IiXSwib3BlcmF0b3ItYXNzaWdubWVudCI6WyJlcnJvciJdLCJvcGVyYXRvci1saW5lYnJlYWsiOlsiZXJyb3IiXSwicGFkZGVkLWJsb2NrcyI6WyJlcnJvciJdLCJwYWRkaW5nLWxpbmUtYmV0d2Vlbi1zdGF0ZW1lbnRzIjpbImVycm9yIl0sInByZWZlci1hcnJvdy1jYWxsYmFjayI6WyJlcnJvciJdLCJwcmVmZXItY29uc3QiOlsiZXJyb3IiXSwicHJlZmVyLWRlc3RydWN0dXJpbmciOlsiZXJyb3IiXSwicHJlZmVyLWV4cG9uZW50aWF0aW9uLW9wZXJhdG9yIjpbImVycm9yIl0sInByZWZlci1uYW1lZC1jYXB0dXJlLWdyb3VwIjpbImVycm9yIl0sInByZWZlci1udW1lcmljLWxpdGVyYWxzIjpbImVycm9yIl0sInByZWZlci1vYmplY3QtaGFzLW93biI6WyJlcnJvciJdLCJwcmVmZXItb2JqZWN0LXNwcmVhZCI6WyJlcnJvciJdLCJwcmVmZXItcHJvbWlzZS1yZWplY3QtZXJyb3JzIjpbImVycm9yIl0sInByZWZlci1yZWZsZWN0IjpbImVycm9yIl0sInByZWZlci1yZWdleC1saXRlcmFscyI6WyJlcnJvciJdLCJwcmVmZXItcmVzdC1wYXJhbXMiOlsiZXJyb3IiXSwicHJlZmVyLXNwcmVhZCI6WyJlcnJvciJdLCJwcmVmZXItdGVtcGxhdGUiOlsiZXJyb3IiXSwicHJlc2VydmUtY2F1Z2h0LWVycm9yIjpbImVycm9yIl0sInF1b3RlLXByb3BzIjpbImVycm9yIl0sInF1b3RlcyI6WyJlcnJvciJdLCJyYWRpeCI6WyJlcnJvciJdLCJyZXF1aXJlLWF0b21pYy11cGRhdGVzIjpbImVycm9yIl0sInJlcXVpcmUtYXdhaXQiOlsiZXJyb3IiXSwicmVxdWlyZS11bmljb2RlLXJlZ2V4cCI6WyJlcnJvciJdLCJyZXF1aXJlLXlpZWxkIjpbImVycm9yIl0sInJlc3Qtc3ByZWFkLXNwYWNpbmciOlsiZXJyb3IiXSwic2VtaSI6WyJlcnJvciJdLCJzZW1pLXNwYWNpbmciOlsiZXJyb3IiXSwic2VtaS1zdHlsZSI6WyJlcnJvciJdLCJzb3J0LWltcG9ydHMiOlsiZXJyb3IiXSwic29ydC1rZXlzIjpbImVycm9yIl0sInNvcnQtdmFycyI6WyJlcnJvciJdLCJzcGFjZS1iZWZvcmUtYmxvY2tzIjpbImVycm9yIl0sInNwYWNlLWJlZm9yZS1mdW5jdGlvbi1wYXJlbiI6WyJlcnJvciJdLCJzcGFjZS1pbi1wYXJlbnMiOlsiZXJyb3IiXSwic3BhY2UtaW5maXgtb3BzIjpbImVycm9yIl0sInNwYWNlLXVuYXJ5LW9wcyI6WyJlcnJvciJdLCJzcGFjZWQtY29tbWVudCI6WyJlcnJvciJdLCJzdHJpY3QiOlsiZXJyb3IiXSwic3dpdGNoLWNvbG9uLXNwYWNpbmciOlsiZXJyb3IiXSwic3ltYm9sLWRlc2NyaXB0aW9uIjpbImVycm9yIl0sInRlbXBsYXRlLWN1cmx5LXNwYWNpbmciOlsiZXJyb3IiXSwidGVtcGxhdGUtdGFnLXNwYWNpbmciOlsiZXJyb3IiXSwidW5pY29kZS1ib20iOlsiZXJyb3IiXSwidXNlLWlzbmFuIjpbImVycm9yIl0sInZhbGlkLXR5cGVvZiI6WyJlcnJvciJdLCJ2YXJzLW9uLXRvcCI6WyJlcnJvciJdLCJ3cmFwLWlpZmUiOlsiZXJyb3IiXSwid3JhcC1yZWdleCI6WyJlcnJvciJdLCJ5aWVsZC1zdGFyLXNwYWNpbmciOlsiZXJyb3IiXSwieW9kYSI6WyJlcnJvciJdfSwibGFuZ3VhZ2VPcHRpb25zIjp7InBhcnNlck9wdGlvbnMiOnsiZWNtYUZlYXR1cmVzIjp7ImpzeCI6dHJ1ZX0sInNvdXJjZVR5cGUiOiJtb2R1bGUifSwicGFyc2VyIjoiQHR5cGVzY3JpcHQtZXNsaW50L3BhcnNlciJ9fX0=">eslint playground</a> <a aria-label="Back to reference 4" class=footnote-backref data-footnote-backref data-footnote-backref-idx=4 href=#fnref-eslint>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Packaging Multiple Dependent Python Modules Using Hatch & Nix]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2025-05-15-packaging-multiple-dependent-python-modules-using-hatch-nix/" />
      <id>https://wolfgirl.dev/blog/2025-05-15-packaging-multiple-dependent-python-modules-using-hatch-nix/</id>
      <published>2025-05-15T11:05:37Z</published>
      <updated>2025-05-15T11:05:37Z</updated>
      <summary><![CDATA[Alternative title: "Overcomplicating My Website Backend Using Hatch & Nix". For the life for me, I could not figure out how to string all...]]></summary>
      <content type="html"><![CDATA[<p>Alternative title: "Overcomplicating My Website Backend Using Hatch & Nix". For the life for me, I could not figure out how to string all of this together until after pouring over the <a href=https://ryantm.github.io/nixpkgs/languages-frameworks/python/>nixpkgs manual</a> & <a href=https://nixos.wiki/wiki/Packaging/Python>NixOS Wiki</a> about 20 times each; no other resource applied cleanly to my situation. Here's hoping these instructions will help someone else going thru the same thing I did.</p><h2><a aria-hidden=true class=anchor href=#basic-setup id=heading-basic-setup></a>Basic Setup</h2><p>Say we have two Python applications that depend on a third, shared Python library. That is, something like the following:</p><pre class=syntax-highlighting><code class=language-python><span class=comment># shared/__init__.py</span>
<span class=constant>NAME</span> <span class=operator>=</span> <span class=string>"PolyWolf"</span></code></pre><pre class=syntax-highlighting><code class=language-python><span class=comment># hello/__main__.py</span>
<span class=keyword>from</span> <span class=variable>shared</span> <span class=keyword>import</span> <span class=constant>NAME</span>
<span class=keyword>if</span> <span class=variable>__name__</span> <span class=operator>==</span> <span class=string>"__main__"</span>:
    <span class=function>print</span>(<span class=string>f"Hello, </span><span class=punctuation>{</span><span class=constant>NAME</span><span class=punctuation>}</span><span class=string>"</span>)</code></pre><pre class=syntax-highlighting><code class=language-python><span class=comment># goodbye/__main__.py</span>
<span class=keyword>from</span> <span class=variable>shared</span> <span class=keyword>import</span> <span class=constant>NAME</span>
<span class=keyword>if</span> <span class=variable>__name__</span> <span class=operator>==</span> <span class=string>"__main__"</span>:
    <span class=function>print</span>(<span class=string>f"Goodbye, </span><span class=punctuation>{</span><span class=constant>NAME</span><span class=punctuation>}</span><span class=string>"</span>)</code></pre><p>Then, we can run them like so:</p><pre class=syntax-highlighting><code>$ nix-shell -p python3 tree
fetching path input 'path:/nix/store/1dwky0bis4bkl3qngsc6pmq902swa9b6-source'

[nix-shell:/tmp/blogpost]$ tree
.
├── goodbye
│   └── __main__.py
├── hello
│   └── __main__.py
└── shared
    └── __init__.py

4 directories, 3 files

[nix-shell:/tmp/blogpost]$ python3 -m hello
Hello, PolyWolf

[nix-shell:/tmp/blogpost]$ python3 -m goodbye
Goodbye, PolyWolf
</code></pre><p>What the programs actually do doesn't matter all too much; what matters is the module structure, and the directory structure. We want <code>shared</code> to be, well, <em>shared</em> between the <code>hello</code> & <code>goodbye</code> programs, and for no one program to be the obvious one to contain all the shared code. This represents how my <a href=https://github.com/p0lyw0lf/crossposter>crossposter</a> is structured, and is representative of many other "monorepo"-style applications too<sup class=footnote-ref><a data-footnote-ref href=#fn-python-vs-js id=fnref-python-vs-js>1</a></sup>.</p><h3><a aria-hidden=true class=anchor href=#pythonpath-and-you id=heading-pythonpath-and-you></a><code>PYTHONPATH</code> And You</h3><p>As a slight aside, there's a specific reason we ran the programs with the <code>-m</code> flag earlier. To see this, let's try running without it:</p><pre class=syntax-highlighting><code>[nix-shell:/tmp/blogpost]$ python3 hello
Traceback (most recent call last):
  File "&lt;frozen runpy>", line 198, in _run_module_as_main
  File "&lt;frozen runpy>", line 88, in _run_code
  File "/tmp/blogpost/hello/__main__.py", line 1, in &lt;module>
    from shared import NAME
ModuleNotFoundError: No module named 'shared'
</code></pre><p>Oh no the dreaded <code>ModuleNotFoundError</code>!! Reading around a bit, we learn the module search path is stored in the <code>sys.path</code> variable, and learn about <a href=https://docs.python.org/3/library/sys_path_init.html>its initialization procedure</a>:</p><blockquote><p>The first entry in the module search path is the directory that contains the input script, if there is one. Otherwise, the first entry is the current directory, which is the case when executing the interactive shell, a -c command, or -m module.</p></blockquote><p>Aha! So that's why: In the <code>-m</code> example, the <code>/tmp/blogpost</code> directory was added to the module path, because there was no "input script", just a module to be run. But without <code>-m</code>, the <code>/tmp/blogpost/hello</code> directory was added, because that's where <code>__main__.py</code> lives, and that directory does <em>not</em> contain a <code>shared</code> module. Is there a workaround? Fortunately, yes, in the next paragraph:</p><blockquote><p>The <code>PYTHONPATH</code> environment variable is often used to add directories to the search path. If this environment variable is found then the contents are added to the module search path.</p></blockquote><p>So! If we really wanted, we could do something like:</p><pre class=syntax-highlighting><code>[nix-shell:/tmp/blogpost]$ PYTHONPATH="$(pwd):$PYTHONPATH" python3 hello
Hello, PolyWolf
</code></pre><p>But honestly, I just use <code>-m</code> :) Anywho, this diversion into <code>PYTHONPATH</code> & module resolution wasn't too relevant, still useful to understand, back to the main content now :)</p><h2><a aria-hidden=true class=anchor href=#transitioning-to-hatch id=heading-transitioning-to-hatch></a>Transitioning To Hatch</h2><p>You may have noticed something about our basic setup: it doesn't create a virtual environment :( This isn't a problem for our extremely simple <code>hello</code> and <code>goodbye</code> programs, but as soon as we need a single dependency outside the standard library, we'll be in trouble.</p><p>For the longest time, I just stuck with the ol' reliable <code>python3 -m venv .venv && source .venv/bin/activate && pip3 -r requirements.txt</code>; <a href=https://docs.python.org/3/library/venv.html>venv</a> is included in the standard library, so it'll Just Work™️ on any Python installation >= 3.3, which is to say, "all of them". There are a bazillion other tools to manage Python virtual environments, like <a href=https://virtualenv.pypa.io/en/latest/user_guide.html>virtualenv</a>, <a href=https://pipenv.pypa.io/en/latest/>pipenv</a>, <a href=https://www.anaconda.com/docs/getting-started/miniconda/main>conda</a>, <a href=https://docs.astral.sh/uv/>uv</a>, & more, but I have never needed their full power, hence, ol' reliable.</p><p>Now, however, with the goal of "creating packages to be built with Nix", we might want something a bit more powerful, something capable of creating Python packages. The modern way to do this is with the <a href=https://packaging.python.org/en/latest/guides/writing-pyproject-toml/>pyproject.toml</a> format, whose guide recommends <a href=https://hatch.pypa.io/latest/>Hatch</a> as the default build backend, so that's what I'll be using for the rest of this "tutorial".</p><p>First, let's make some more directories, and move some files around. Why we're doing this will become apparent shortly:</p><pre class=syntax-highlighting><code>[nix-shell:/tmp/blogpost]$ for lib in shared hello goodbye; do
> mkdir -p $lib/src/$lib
> mv $lib/*.py $lib/src/$lib
> done
</code></pre><p>Then, let's define our pyproject.toml for these new folders:</p><pre class=syntax-highlighting><code class=language-toml><span class=comment># shared/pyproject.toml</span>
<span class=punctuation>[</span><span class=property>build-system</span><span class=punctuation>]</span>
<span class=property>requires</span> <span class=operator>=</span> <span class=punctuation>[</span><span class=string>"hatchling >= 1.26"</span><span class=punctuation>]</span>
<span class=property>build-backend</span> <span class=operator>=</span> <span class=string>"hatchling.build"</span>

<span class=punctuation>[</span><span class=property>project</span><span class=punctuation>]</span>
<span class=property>name</span> <span class=operator>=</span> <span class=string>"shared"</span>
<span class=property>version</span> <span class=operator>=</span> <span class=string>"0.1.0"</span></code></pre><pre class=syntax-highlighting><code class=language-toml><span class=comment># {hello,goodbye}/pyproject.toml</span>
<span class=punctuation>[</span><span class=property>build-system</span><span class=punctuation>]</span>
<span class=property>requires</span> <span class=operator>=</span> <span class=punctuation>[</span><span class=string>"hatchling >= 1.26"</span><span class=punctuation>]</span>
<span class=property>build-backend</span> <span class=operator>=</span> <span class=string>"hatchling.build"</span>

<span class=punctuation>[</span><span class=property>project</span><span class=punctuation>]</span>
<span class=property>name</span> <span class=operator>=</span> <span class=string>"hello"</span> <span class=comment># goodbye/pyproject.toml is identical, save this line</span>
<span class=property>version</span> <span class=operator>=</span> <span class=string>"0.1.0"</span>
<span class=property>dependencies</span> <span class=operator>=</span> <span class=punctuation>[</span>
  <span class=string>"shared @ {root:parent:uri}/shared"</span><span class=punctuation>,</span> <span class=comment># ":parent" is equivalent to "../", and can be stacked as many times as required</span>
<span class=punctuation>]</span>

<span class=punctuation>[</span><span class=property>tool</span><span class=punctuation>.</span><span class=property>hatch</span><span class=punctuation>.</span><span class=property>metadata</span><span class=punctuation>]</span>
<span class=property>allow-direct-references</span> <span class=operator>=</span> <span class=constant>true</span></code></pre><p>We end up with a directory structure that looks like this:</p><pre class=syntax-highlighting><code>[nix-shell:/tmp/blogpost]$ tree
.
├── goodbye
│   ├── pyproject.toml
│   └── src
│       └── goodbye
│           └── __main__.py
├── hello
│   ├── pyproject.toml
│   └── src
│       └── hello
│           └── __main__.py
└── shared
    ├── pyproject.toml
    └── src
        └── shared
            └── __init__.py

10 directories, 6 files
</code></pre><p>We've created 3 separate "projects", and then put our files within each project in such a way that Hatch expects to be able to build a package from them.</p><p>So! Let us now create a virtual environment, build a package, and run it, all in one handy command:</p><pre class=syntax-highlighting><code>[nix-shell:/tmp/blogpost]$ exit

$ nix-shell -p hatch tree
fetching path input 'path:/nix/store/1dwky0bis4bkl3qngsc6pmq902swa9b6-source'

[nix-shell:/tmp/blogpost]$ cd hello

[nix-shell:/tmp/blogpost/hello]$ hatch run -- python3 -m hello
  error: subprocess-exited-with-error

  × Preparing editable metadata (pyproject.toml) did not run successfully.
  │ exit code: 1
  ╰─> [56 lines of output]
      Traceback (most recent call last):
        File "/home/nixos/.local/share/hatch/env/virtual/hello/DMd9LDvE/hello/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 167, in prepare_metadata_for_build_editable
...
</code></pre><p><em><strong>AAAAAAAAAA</strong></em></p><pre class=syntax-highlighting><code>...
      ValueError: Unable to determine which files to ship inside the wheel using the following heuristics: https://hatch.pypa.io/latest/plugins/builder/wheel/#default-file-selection

      The most likely cause of this is that there is no directory that matches the name of your project (hello).

      At least one file selection option must be defined in the `tool.hatch.build.targets.wheel` table, see: https://hatch.pypa.io/latest/config/build/

      As an example, if you intend to ship a directory named `foo` that resides within a `src` directory located at the root of your project, you can define the following:

      [tool.hatch.build.targets.wheel]
      packages = ["src/foo"]
      [end of output]
...
</code></pre><p>aaaaa oh wait ok I think I know why this is happening: we forgot to provide an empty <code>__init__.py</code> file to appease the Python Gods. The Python Gods are displeased when a folder acts as a module & does not contain an <code>__init__.py</code> file, and we only avoided their wrath earlier because our earthly machinations provided no threat to their heavenly realm; now that we have dared snatch the fire that is "build tool that manages a virtual environment" from their palace, the only way to placate them is to fix our directory structure, <em>not</em> to torture our config file like their oracles suggested. Allow me to demonstrate:</p><pre class=syntax-highlighting><code>[nix-shell:/tmp/blogpost]$ for lib in hello goodbye; do touch $lib/src/$lib/__init__.py; done

[nix-shell:/tmp/blogpost]$ tree
.
├── goodbye
│   ├── pyproject.toml
│   └── src
│       └── goodbye
│           ├── __init__.py
│           └── __main__.py
├── hello
│   ├── pyproject.toml
│   └── src
│       └── hello
│           ├── __init__.py
│           └── __main__.py
└── shared
    ├── pyproject.toml
    └── src
        └── shared
            └── __init__.py

10 directories, 8 files
</code></pre><p>And now, we should surely be able to run:</p><pre class=syntax-highlighting><code>[nix-shell:/tmp/blogpost/hello]$ hatch run -- python3 -m hello
/home/nixos/.local/share/hatch/env/virtual/hello/DMd9LDvE/hello/bin/python3: No module named hello
</code></pre><p>Ah, right, stale environment:</p><pre class=syntax-highlighting><code>[nix-shell:/tmp/blogpost/hello]$ hatch env prune

[nix-shell:/tmp/blogpost/hello]$ hatch run -- python3 -m hello
Hello, PolyWolf
</code></pre><p>Big success!!<sup class=footnote-ref><a data-footnote-ref href=#fn-errors id=fnref-errors>2</a></sup> 🐍, amirite? :P</p><h2><a aria-hidden=true class=anchor href=#time-to-nix-it-up id=heading-time-to-nix-it-up></a>Time To Nix It Up</h2><p>Referencing <a href=https://hachyderm.io/@leftpaddotpy>Jade</a>'s excellent resource on <a href=https://jade.fyi/blog/flakes-arent-real/>using Nix flakes the non-flake way</a>, let's set up a simple devshell<sup class=footnote-ref><a data-footnote-ref href=#fn-more-nix-shells id=fnref-more-nix-shells>3</a></sup>:</p><pre class=syntax-highlighting><code class=language-nix><span class=comment># flake.nix</span>
<span class=punctuation>{</span>
  <span class=variable>inputs</span> <span class=punctuation>=</span> <span class=punctuation>{</span>
    <span class=variable>nixpkgs</span><span class=punctuation>.</span><span class=variable>url</span> <span class=punctuation>=</span> <span class=string>"github:NixOS/nixpkgs/nixos-unstable"</span><span class=punctuation>;</span>
    <span class=variable>flake-utils</span><span class=punctuation>.</span><span class=variable>url</span> <span class=punctuation>=</span> <span class=string>"github:numtide/flake-utils"</span><span class=punctuation>;</span>
  <span class=punctuation>};</span>

  <span class=variable>outputs</span> <span class=punctuation>=</span> <span class=punctuation>{</span> <span class=variable>self</span><span class=punctuation>,</span> <span class=variable>nixpkgs</span><span class=punctuation>,</span> <span class=variable>flake-utils</span> <span class=punctuation>}</span>: <span class=variable>flake-utils</span><span class=punctuation>.</span><span class=variable>lib</span><span class=punctuation>.</span><span class=variable>eachDefaultSystem</span> <span class=punctuation>(</span><span class=variable>system</span>:
    <span class=keyword>let</span>
      <span class=variable>pkgs</span> <span class=punctuation>=</span> <span class=variable>import</span> <span class=variable>nixpkgs</span> <span class=punctuation>{</span> <span class=keyword>inherit</span> <span class=variable>system</span><span class=punctuation>;</span> <span class=punctuation>};</span>
    <span class=keyword>in</span>
      <span class=punctuation>{</span>
        <span class=variable>devShells</span><span class=punctuation>.</span><span class=variable>default</span> <span class=punctuation>=</span> <span class=variable>pkgs</span><span class=punctuation>.</span><span class=variable>callPackage</span> <span class=string>./shell.nix</span> <span class=punctuation>{</span> <span class=punctuation>};</span>
      <span class=punctuation>}</span>
  <span class=punctuation>);</span>
<span class=punctuation>}</span></code></pre><pre class=syntax-highlighting><code class=language-nix><span class=comment># shell.nix</span>
<span class=punctuation>{</span> <span class=variable>pkgs</span> <span class=punctuation>?</span> <span class=variable>import</span> <span class=string>&lt;nixpkgs></span> <span class=punctuation>{</span> <span class=punctuation>}</span> <span class=punctuation>}</span>:
<span class=variable>pkgs</span><span class=punctuation>.</span><span class=variable>mkShell</span> <span class=punctuation>{</span>
  <span class=variable>packages</span> <span class=punctuation>=</span> <span class=punctuation>(</span>
    <span class=keyword>with</span> <span class=variable>pkgs</span><span class=punctuation>;</span>
    <span class=punctuation>[</span>
      <span class=comment># The python virtual environments themselves are managed with hatch, no need for any other dependencies in this file! Unless you want to put like, language server stuff here ofc</span>
      <span class=variable>hatch</span>
    <span class=punctuation>]</span>
  <span class=punctuation>);</span>
<span class=punctuation>}</span></code></pre><p>And for this next part, it would be very useful for to do a bit of reading on Nix before continuing; I recommend the nix.dev tutorials on <a href=https://nix.dev/tutorials/callpackage><code>callPackage</code></a> + <a href=https://nix.dev/tutorials/working-with-local-files>local files</a>, the NixOS Wiki page on <a href=https://wiki.nixos.org/wiki/Overlays>Overlays</a>, & all the basic Nix syntax ones. Otherwise, this will just be blind copy-pasting:</p><pre class=syntax-highlighting><code class=language-nix><span class=comment># shared/package.nix</span>
<span class=punctuation>{</span>
  <span class=variable>lib</span><span class=punctuation>,</span>
  <span class=variable>buildPythonPackage</span><span class=punctuation>,</span>
  <span class=variable>hatchling</span><span class=punctuation>,</span><span class=variable>
</span><span class=punctuation>}</span><span class=variable>:
</span><span class=variable>buildPythonPackage</span><span class=variable> </span><span class=punctuation>{</span><span class=variable>
  </span><span class=variable>pname</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=string>"shared"</span><span class=punctuation>;</span><span class=variable>
  </span><span class=variable>version</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=string>"0.1.0"</span><span class=punctuation>;</span><span class=variable>

  </span><span class=variable>src</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=variable>lib</span><span class=punctuation>.</span><span class=variable>fileset</span><span class=punctuation>.</span><span class=variable>toSource</span><span class=variable> </span><span class=punctuation>{</span><span class=variable>
    </span><span class=variable>root</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=string>./.</span><span class=punctuation>;</span><span class=variable>
    </span><span class=variable>fileset</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=variable>lib</span><span class=punctuation>.</span><span class=variable>fileset</span><span class=punctuation>.</span><span class=variable>gitTracked</span><span class=variable> </span><span class=string>./.</span><span class=punctuation>;</span><span class=variable>
  </span><span class=punctuation>};</span><span class=variable>

  </span><span class=variable>pyproject</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=variable>true</span><span class=punctuation>;</span><span class=variable>
  </span><span class=variable>build-system</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=punctuation>[</span><span class=variable> </span><span class=variable>hatchling</span><span class=variable> </span><span class=punctuation>];</span><span class=variable>
</span><span class=punctuation>}</span></code></pre><pre class=syntax-highlighting><code class=language-nix><span class=comment># {hello,goodbye}/package.nix</span>
<span class=punctuation>{</span>
  <span class=variable>lib</span><span class=punctuation>,</span>
  <span class=variable>buildPythonPackage</span><span class=punctuation>,</span>
  <span class=variable>hatchling</span><span class=punctuation>,</span>
  <span class=variable>myproject-shared</span><span class=punctuation>,</span> <span class=comment># Here, we take the package defined by shared/package.nix as a dependency, to be hooked up later</span><span class=variable>
</span><span class=punctuation>}</span><span class=variable>:
</span><span class=variable>buildPythonPackage</span><span class=variable> </span><span class=punctuation>{</span><span class=variable>
  </span><span class=variable>pname</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=string>"hello"</span><span class=punctuation>;</span><span class=variable> </span><span class=comment># goodbye/package.nix is identical, save this line</span><span class=variable>
  </span><span class=variable>version</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=string>"0.1.0"</span><span class=punctuation>;</span><span class=variable>

  </span><span class=variable>src</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=variable>lib</span><span class=punctuation>.</span><span class=variable>fileset</span><span class=punctuation>.</span><span class=variable>toSource</span><span class=variable> </span><span class=punctuation>{</span><span class=variable>
    </span><span class=variable>root</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=string>./.</span><span class=punctuation>;</span><span class=variable>
    </span><span class=variable>fileset</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=variable>lib</span><span class=punctuation>.</span><span class=variable>fileset</span><span class=punctuation>.</span><span class=variable>gitTracked</span><span class=variable> </span><span class=string>./.</span><span class=punctuation>;</span><span class=variable>
  </span><span class=punctuation>};</span><span class=variable>

  </span><span class=variable>pyproject</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=variable>true</span><span class=punctuation>;</span><span class=variable>
  </span><span class=variable>build-system</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=punctuation>[</span><span class=variable> </span><span class=variable>hatchling</span><span class=variable> </span><span class=punctuation>];</span><span class=variable>

  </span><span class=variable>propagatedBuildInputs</span><span class=variable> </span><span class=punctuation>=</span><span class=variable> </span><span class=punctuation>[</span><span class=variable> </span><span class=variable>myproject-shared</span><span class=variable> </span><span class=punctuation>];</span><span class=variable> </span><span class=comment># Here, we declare how exactly we depend on the package defined by shared/package.nix; we'll get into what this means later</span><span class=variable>
</span><span class=punctuation>}</span></code></pre><p>Whoa that's it!! For the Nixpkgs-style files, at least. Yeah, turns out using Hatch took care of most of the hard part of packaging, the rest is built-in to Nixpkgs itself. We do still have to repeat the dependencies of each project outside of the pyproject.toml<sup class=footnote-ref><a data-footnote-ref href=#fn-repeat-dependencies id=fnref-repeat-dependencies>4</a></sup>, which is a bummer, but oh well the rest of the file is quite small which is good, thank u Nixpkgs</p><p>With all these files in place, our tree now looks like:</p><pre class=syntax-highlighting><code>[nix-shell:/tmp/blogpost]$ tree
.
├── flake.lock
├── flake.nix
├── goodbye
│   ├── package.nix
│   ├── pyproject.toml
│   └── src
│       └── goodbye
│           ├── __init__.py
│           └── __main__.py
├── hello
│   ├── package.nix
│   ├── pyproject.toml
│   └── src
│       └── hello
│           ├── __init__.py
│           └── __main__.py
├── shared
│   ├── package.nix
│   ├── pyproject.toml
│   └── src
│       └── shared
│           └── __init__.py
└── shell.nix

10 directories, 14 files
</code></pre><p>Next, we have to wire all these packages up inside our <code>flake.nix</code> so we can actually build them. These are fairly unusual packages, turns out; they use a top-level <code>buildPythonPackage</code> variable that doesn't exist in a normal Nixpkgs <code>callPackage</code>. This aligns with the <a href=https://ryantm.github.io/nixpkgs/languages-frameworks/python/>Nixpkgs manual</a>: Python packages are special, because they could be built for multiple versions of the interpreter, and this is the easiest way to do that without having to do something even crazier w/ overrides.</p><p>Anyways, here's our updated <code>flake.nix</code>, then an explanation:</p><pre class=syntax-highlighting><code class=language-nix><span class=comment># flake.nix</span>
<span class=punctuation>{</span>
  <span class=variable>inputs</span> <span class=punctuation>=</span> <span class=punctuation>{</span>
    <span class=variable>nixpkgs</span><span class=punctuation>.</span><span class=variable>url</span> <span class=punctuation>=</span> <span class=string>"github:NixOS/nixpkgs/nixos-unstable"</span><span class=punctuation>;</span>
    <span class=variable>flake-utils</span><span class=punctuation>.</span><span class=variable>url</span> <span class=punctuation>=</span> <span class=string>"github:numtide/flake-utils"</span><span class=punctuation>;</span>
  <span class=punctuation>};</span>

  <span class=variable>outputs</span> <span class=punctuation>=</span> <span class=punctuation>{</span> <span class=variable>self</span><span class=punctuation>,</span> <span class=variable>nixpkgs</span><span class=punctuation>,</span> <span class=variable>flake-utils</span> <span class=punctuation>}</span>: <span class=variable>flake-utils</span><span class=punctuation>.</span><span class=variable>lib</span><span class=punctuation>.</span><span class=variable>eachDefaultSystem</span> <span class=punctuation>(</span><span class=variable>system</span>:
    <span class=keyword>let</span>
      <span class=variable>pkgs</span> <span class=punctuation>=</span> <span class=variable>import</span> <span class=variable>nixpkgs</span> <span class=punctuation>{</span> <span class=keyword>inherit</span> <span class=variable>system</span><span class=punctuation>;</span> <span class=punctuation>};</span>
      <span class=variable>python3</span> <span class=punctuation>=</span> <span class=variable>pkgs</span><span class=punctuation>.</span><span class=variable>python3</span><span class=punctuation>.</span><span class=variable>override</span> <span class=punctuation>{</span>
        <span class=variable>packageOverrides</span> <span class=punctuation>=</span> <span class=variable>final</span>: <span class=variable>prev</span>: <span class=punctuation>{</span>
          <span class=variable>myproject-shared</span> <span class=punctuation>=</span> <span class=variable>final</span><span class=punctuation>.</span><span class=variable>callPackage</span> <span class=string>./shared/package.nix</span> <span class=punctuation>{</span> <span class=punctuation>};</span>
          <span class=variable>myproject-hello</span> <span class=punctuation>=</span> <span class=variable>final</span><span class=punctuation>.</span><span class=variable>callPackage</span> <span class=string>./hello/package.nix</span> <span class=punctuation>{</span>
            <span class=variable>myproject-shared</span> <span class=punctuation>=</span> <span class=variable>final</span><span class=punctuation>.</span><span class=variable>myproject-shared</span><span class=punctuation>;</span>
          <span class=punctuation>};</span>
          <span class=variable>myproject-goodbye</span> <span class=punctuation>=</span> <span class=variable>final</span><span class=punctuation>.</span><span class=variable>callPackage</span> <span class=string>./goodbye/package.nix</span> <span class=punctuation>{</span>
            <span class=variable>myproject-shared</span> <span class=punctuation>=</span> <span class=variable>final</span><span class=punctuation>.</span><span class=variable>myproject-shared</span><span class=punctuation>;</span>
          <span class=punctuation>};</span>
        <span class=punctuation>};</span>
      <span class=punctuation>};</span>
    <span class=keyword>in</span>
    <span class=punctuation>{</span>
      <span class=variable>packages</span> <span class=punctuation>=</span> <span class=punctuation>{</span> <span class=keyword>inherit</span> <span class=variable>python3</span><span class=punctuation>;</span> <span class=punctuation>};</span>
      <span class=variable>devShells</span><span class=punctuation>.</span><span class=variable>default</span> <span class=punctuation>=</span> <span class=variable>pkgs</span><span class=punctuation>.</span><span class=variable>callPackage</span> <span class=string>./shell.nix</span> <span class=punctuation>{</span> <span class=punctuation>};</span>
    <span class=punctuation>}</span>
  <span class=punctuation>);</span>
<span class=punctuation>}</span></code></pre><p>Oh wow that's dense! Let's split it up a bit:</p><pre class=syntax-highlighting><code class=language-nix><span class=keyword>let</span>
  <span class=variable>python3</span> <span class=punctuation>=</span> <span class=variable>pkgs</span><span class=punctuation>.</span><span class=variable>python3</span><span class=punctuation>.</span><span class=variable>override</span> <span class=punctuation>{</span>
    <span class=comment># ...</span>
  <span class=punctuation>};</span>
<span class=keyword>in</span>
<span class=punctuation>{</span>
  <span class=variable>packages</span> <span class=punctuation>=</span> <span class=punctuation>{</span> <span class=keyword>inherit</span> <span class=variable>python3</span><span class=punctuation>;</span> <span class=punctuation>};</span>
<span class=punctuation>}</span></code></pre><p>This declares a new <code>python3</code> package in our Flake, defined as an override of the base <code>python3</code> package, whose final <code>pkgs</code> attribute will contain our custom packages. And how do we declare those? With the <a href=https://nixos.wiki/wiki/Overlays>overlay</a> pattern!</p><pre class=syntax-highlighting><code class=language-nix><span class=variable>packageOverrides</span> <span class=punctuation>=</span> <span class=variable>final</span>: <span class=variable>prev</span>: <span class=punctuation>{</span>
  <span class=variable>myproject-shared</span> <span class=punctuation>=</span> <span class=variable>final</span><span class=punctuation>.</span><span class=variable>callPackage</span> <span class=string>./shared/package.nix</span> <span class=punctuation>{</span> <span class=punctuation>};</span>
  <span class=variable>myproject-hello</span> <span class=punctuation>=</span> <span class=variable>final</span><span class=punctuation>.</span><span class=variable>callPackage</span> <span class=string>./hello/package.nix</span> <span class=punctuation>{</span>
    <span class=variable>myproject-shared</span> <span class=punctuation>=</span> <span class=variable>final</span><span class=punctuation>.</span><span class=variable>myproject-shared</span><span class=punctuation>;</span>
  <span class=punctuation>};</span>
  <span class=variable>myproject-goodbye</span> <span class=punctuation>=</span> <span class=variable>final</span><span class=punctuation>.</span><span class=variable>callPackage</span> <span class=string>./goodbye/package.nix</span> <span class=punctuation>{</span>
    <span class=variable>myproject-shared</span> <span class=punctuation>=</span> <span class=variable>final</span><span class=punctuation>.</span><span class=variable>myproject-shared</span><span class=punctuation>;</span>
  <span class=punctuation>};</span>
<span class=punctuation>};</span></code></pre><p>Each package that we want to add gets <code>callPackage</code>-ed<sup class=footnote-ref><a data-footnote-ref href=#fn-final-callPackage id=fnref-final-callPackage>5</a></sup>, with its dependencies injected from the "final" resolved set of packages. This is just the Nix(pkgs)-y way of doing dependency tracking such that any dependency can be overridden at arbitrarily deep levels. Fortunately, we're just adding new packages, so we don't have to think <em>too</em> hard about that complexity here.</p><p>With that all in place, let's try to build one of our packages:</p><pre class=syntax-highlighting><code>[nix-shell:/tmp/blogpost]$ nix build .#python3.pkgs.myproject-hello

[nix-shell:/tmp/blogpost]$ ls result
lib  nix-support

[nix-shell:/tmp/blogpost]$ ls result/lib/python3*/site-packages
hello  hello-0.1.0.dist-info
</code></pre><p>Huh! We might have expected a <code>shared</code> module to appear here too, given we declared it as a dependency. I guess it doesn't do those just yet... Maybe it'll be complete in the main package?</p><pre class=syntax-highlighting><code>[nix-shell:/tmp/blogpost]$ nix build .#python3

[nix-shell:/tmp/blogpost]$ ls result
bin  include  lib  nix-support  share

[nix-shell:/tmp/blogpost]$ cd result/bin

[nix-shell:/tmp/blogpost/result/bin]$ ./python3 -m hello
/nix/store/kjvgj2n3yn70hmjifg6y0bk9m4rf7jba-python3-3.12.10/bin/python3: No module named hello
</code></pre><p>No, that works even less! I guess that makes sense, it's not like the default installation of python3 comes with <em>every Python package ever built</em> by default, we'll need to do something to explicitly install those somehow. But this begs the question: how <em>do</em> Nixpkgs Python dependencies work, anyways?</p><h3><a aria-hidden=true class=anchor href=#she-propagate-on-my-build-input-till-i-fixed-point id=heading-she-propagate-on-my-build-input-till-i-fixed-point></a>She Propagate On My Build Input Till I Fixed Point</h3><p>Referencing <a href=https://ryantm.github.io/nixpkgs/languages-frameworks/python/#buildpythonpackage-parameters>the reference</a>:</p><blockquote><ul><li><code>nativeBuildInputs ? []</code>: Build-time only dependencies. Typically executables as well as the items listed in <code>setup_requires</code>.</li><li><code>buildInputs ? []</code>: Build and/or run-time dependencies that need to be compiled for the host machine. Typically non-Python libraries which are being linked.</li><li><code>nativeCheckInputs ? []</code>: Dependencies needed for running the <code>checkPhase</code>. These are added to <code>nativeBuildInputs</code> when <code>doCheck = true</code>. Items listed in <code>tests_require</code> go here.</li><li><code>propagatedBuildInputs ? []</code>: Aside from propagating dependencies, <code>buildPythonPackage</code> also injects code into and wraps executables with the paths included in this list. Items listed in <code>install_requires</code> go here.</li></ul></blockquote><p>And summarizing in simpler English: use <code>propagatedBuildInputs</code> for Python library dependencies. Okie dokie! I'm sure there are various reasons for this, but we did that earlier so we're on the right track.</p><p>But wait a minute, doing a normal <code>nix build</code> did <em>not</em> propagate this build input into the <code>result</code> directory. What gives? This confused me for a long while<sup class=footnote-ref><a data-footnote-ref href=#fn-nixpkgs-confusing id=fnref-nixpkgs-confusing>6</a></sup>, until I started looking at things from the perspective of a C compiler.</p><p>Basically: Nix was originally meant for packaging C code, so a lot of its semantics make more sense when viewed that way. Packages put in <code>buildInputs</code> will be built, put into <code>/nix/store</code>, their paths exposed in certain environment variables, and then it's up to whatever linker to actually hard-code those paths into the final executable. However, there is no "final executable" at the Python library level, hence why we put the dependencies into <code>propagatedBuildInputs</code> so they'll be available by the time we get around to creating a Python environment. A Python environment can be thought of like an executable in this framework; a script can "link" (literally, create symlinks to) all specified packages into the directory structure Python expects.</p><p>Quite fortunately, our goal of "link a Python package & all of its dependencies into a functioning Python environment" already has a simple solution:</p><pre class=syntax-highlighting><code class=language-nix><span class=comment># flake.nix</span>
<span class=comment># ...</span>
      in
      <span class=punctuation>{</span>
        <span class=variable>packages</span> <span class=punctuation>=</span> <span class=punctuation>{</span>
          <span class=keyword>inherit</span> <span class=variable>python3</span><span class=punctuation>;</span>
          <span class=variable>python3-myproject-hello</span> <span class=punctuation>=</span> <span class=variable>python3</span><span class=punctuation>.</span><span class=variable>withPackages</span> <span class=punctuation>(</span><span class=variable>ps</span>: <span class=punctuation>[</span> <span class=variable>ps</span><span class=punctuation>.</span><span class=variable>myproject-hello</span> <span class=punctuation>]);</span>
          <span class=variable>python3-myproject-goodbye</span> <span class=punctuation>=</span> <span class=variable>python3</span><span class=punctuation>.</span><span class=variable>withPackages</span> <span class=punctuation>(</span><span class=variable>ps</span>: <span class=punctuation>[</span> <span class=variable>ps</span><span class=punctuation>.</span><span class=variable>myproject-goodbye</span> <span class=punctuation>]);</span>
        <span class=punctuation>};</span>
        <span class=variable>devShells</span><span class=punctuation>.</span><span class=variable>default</span> <span class=punctuation>=</span> <span class=variable>pkgs</span><span class=punctuation>.</span><span class=variable>callPackage</span> <span class=string>./shell.nix</span> <span class=punctuation>{</span> <span class=punctuation>};</span>
      <span class=punctuation>}</span>
<span class=comment># ...</span></code></pre><p>This <code>python3.withPackages</code> function does exactly what we need!! Magic. Now, when we build & run:</p><pre class=syntax-highlighting><code>[nix-shell:/tmp/blogpost]$ nix build .#python3-myproject-hello

[nix-shell:/tmp/blogpost]$ cd result/bin

[nix-shell:/tmp/blogpost/result/bin]$ ./python3 -m hello
Hello, PolyWolf
</code></pre><p>It works! Yay!!</p><h2><a aria-hidden=true class=anchor href=#drawbacks id=heading-drawbacks></a>Drawbacks</h2><p>Now, by no means do I intend to represent that this approach is the perfect way of doing things with Python + Nix. Far from it, really. Here are some of rough edges I've run into in this workflow that I'd like to eventually sand off:</p><ul><li><code>shared</code>, <code>hello</code>, and <code>goodbye</code> have to live in those strange <code>$lib/src/$lib</code> directories. This is solely because pyproject.toml doesn't have good multi-package support; Nix itself is flexible enough, but I am relying on pyproject.toml to make the Nix packaging easier.</li><li>As mentioned earlier, we have to specify all dependencies twice: once in <code>pyproject.toml</code>, and once in <code>package.nix</code>, which is sad. Also, if there are any version constraints in <code>pyproject.toml</code>, those will effectively break <code>package.nix</code> because Nixpkgs only contains one version of each package.</li><li>If <code>shared</code> ever changes, the hatch environments for <code>hello</code> and <code>goodbye</code> will need to be deleted and re-recreated. Hatch itself seems to have a "development mode" where changed to the current project are reflected in the virtual environment, but it doesn't seem like that applies to this multi-project usecase.</li></ul><p>Still, this is just about good enough for my project, so I think I will stop here. All this Nix packaging is just so I can write a <a href=https://github.com/p0lyw0lf/infrastructure/tree/d430367a2e3f9aa95783314451d1509446ea5e16/nixos>NixOS module</a> and finally start deploying my site the Cool way (<code>nixos-rebuild switch --flake .#devbox --target-host devbox</code>) instead of the Boring way (<code>ssh devbox 'cd crossposter; git pull; systemctl --user restart rc.wolfgirl.dev'</code>). Join me next time for that, I guess?? Assuming I don't get sidetracked on something else first lol</p><section class=footnotes data-footnotes><ol><li id=fn-python-vs-js><p>From what I understand, this is a much more common pattern in the Javascript & Rust worlds, where the multi-module build tooling is actually somewhat decent. No matter, we press onwards regardless of our fate here. <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-python-vs-js>↩</a></p></li><li id=fn-errors><p>I hope showing these errors is useful, by the way. I know most tutorials just show the happy path, but I think it's also important to show the common error paths too, and ways off of them back onto the happy path. <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-errors>↩</a></p></li><li id=fn-more-nix-shells><p>For longer reads on Nix devshells that go way too into the details (/pos) I highly recommend fasterthanlime's post series on the <a href=https://fasterthanli.me/series/building-a-rust-service-with-nix/part-9>non-flake way</a> and the <a href=https://fasterthanli.me/series/building-a-rust-service-with-nix/part-10>flake way</a>. These are more targeted at Rust devshells and also maybe slightly out-of-date but that's ok the basics are still very pertinent & Amos is a very good writer. <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-more-nix-shells>↩</a></p></li><li id=fn-repeat-dependencies><p>I'm sure a sufficiently motivated nix-er could figure out a way to parse the pyproject.toml at evaluation time, but I am not that. Oh well! <a aria-label="Back to reference 4" class=footnote-backref data-footnote-backref data-footnote-backref-idx=4 href=#fnref-repeat-dependencies>↩</a></p></li><li id=fn-final-callPackage><p>Expert Nix users will note the <code>final.callPackage</code> calls are a bit strange, when usually overlays use <code>prev.callPackage</code> for various reasons beyond my ken. For another reason even further beyond my ken, <code>prev.callPackage</code> doesn't exist in this place, so we use <code>final.callPackage</code> instead ya <a aria-label="Back to reference 5" class=footnote-backref data-footnote-backref data-footnote-backref-idx=5 href=#fnref-final-callPackage>↩</a></p></li><li id=fn-nixpkgs-confusing><p>Pro tip: Don't read the <a href=https://nixos.org/manual/nixpkgs/stable/#ssec-stdenv-dependencies-propagated>relevant section of the nixpkgs manual</a> when first learning how dependencies work. Only served to confuse me more tbh <a aria-label="Back to reference 6" class=footnote-backref data-footnote-backref data-footnote-backref-idx=6 href=#fnref-nixpkgs-confusing>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Deriving `Functor` in Rust]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2025-04-14-deriving-functor-in-rust/" />
      <id>https://wolfgirl.dev/blog/2025-04-14-deriving-functor-in-rust/</id>
      <published>2025-04-14T15:37:56Z</published>
      <updated>2025-04-14T15:37:56Z</updated>
      <summary><![CDATA[Wow it's been a hot minute! I kept meaning to write about this, but each time I sat down, I came up with a new idea that I just _had_ to...]]></summary>
      <content type="html"><![CDATA[<p>Wow it's been a hot minute! I kept meaning to write about this, but each time I sat down, I came up with a new idea that I just <em>had</em> to try out. At this point, I have tried enough things, and am reasonably confident all my explanations will be correct. So, without further ado,</p><h2><a aria-hidden=true class=anchor href=#problem-statement id=heading-problem-statement></a>Problem Statement</h2><p>Given:</p><ul><li>A finite set of <code>struct</code>s/<code>enum</code>s (henceforth referred to as "items"), possibly generic</li></ul><p>Create:</p><ul><li>A way to, for each item in the set, recursively transform any other item, including inner generic parameters, if possible.</li></ul><p>Let's take a look at a simple example:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=property>val</span><span class=punctuation>:</span> <span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>,</span>
    <span class=property>next</span><span class=punctuation>:</span> <span class=type>Option</span><span class=punctuation>&lt;</span><span class=type>Box</span><span class=punctuation>&lt;</span><span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>>></span>
<span class=punctuation>}</span>
<span class=keyword>struct</span> <span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>(</span><span class=type>T</span><span class=punctuation>);</span></code></pre><p>Then, given a <code>ListNode&lt;T></code>, we'd like to be able to turn it into a <code>ListNode&lt;U></code>, just by specifying how to turn <code>Val&lt;T></code> into <code>Val&lt;U></code>.</p><h2><a aria-hidden=true class=anchor href=#approach-1-write-a-traversal-manually id=heading-approach-1-write-a-traversal-manually></a>Approach 1: Write a traversal manually</h2><pre class=syntax-highlighting><code class=language-rust><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>transform</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>)</span> -> <span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>)</span> -> <span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>></span> <span class=punctuation>{</span>
        <span class=type>ListNode</span> <span class=punctuation>{</span>
            <span class=property>val</span><span class=punctuation>:</span> <span class=function>f</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>.</span><span class=property>val</span><span class=punctuation>),</span>
            <span class=property>next</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=property>next</span><span class=punctuation>.</span><span class=function>map</span><span class=punctuation>(</span>|n| <span class=type>Box</span><span class=punctuation>::</span><span class=function>new</span><span class=punctuation>((</span><span class=operator>*</span>n<span class=punctuation>).</span><span class=function>transform</span><span class=punctuation>(</span>f<span class=punctuation>))),</span>
        <span class=punctuation>}</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>Great! Nice & simple. However, manually mapping through the <code>Option</code> and <code>Box</code> is a bit tedious, especially if we want to write other, similar transforms. Let's see if we can solve that.</p><h2><a aria-hidden=true class=anchor href=#approach-2-functor-time id=heading-approach-2-functor-time></a>Approach 2: <code>Functor</code> time!!</h2><p>In my <a href=https://wolfgirl.dev/blog/2024-11-24-a-novel-idea-about-functor-in-rust/>last post on this subject</a>, I covered this crazy wacky trait I called <code>Functor</code>, invented specifically to do these sorts of transforms. Here's the trait definition<sup class=footnote-ref><a data-footnote-ref href=#fn-functor id=fnref-functor>1</a></sup>:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>;</span>
<span class=punctuation>}</span></code></pre><p>And here's how it's used (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=986293cbdec0b24e8f2f7a3f3406067e">playground link</a>), with doc comments explaining what each part means:</p><pre class=syntax-highlighting><code class=language-rust><span class=comment>/// There are 4 types involved in any Functor implementation: {Input, Output} x {Inner, Container}.
/// What a Functor implementation means is, given a function InputInner -> OutputInner, you can write a function InputContainer -> OutputContainer.
/// Writing the full name of each type is a bit hard, so we have shorthand:
/// + InputInner = Input (Val&lt;T>)
/// + OutputInner = Output (Val&lt;U>)
/// + InputContainer = Self (ListNode&lt;T>)
/// + OutputContainer = Mapped (ListNode&lt;U>)
</span><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>></span> <span class=keyword>for</span> <span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=keyword>mut</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>)</span> -> <span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>)</span> -> <span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>></span> <span class=punctuation>{</span>
        <span class=type>ListNode</span> <span class=punctuation>{</span>
            <span class=property>val</span><span class=punctuation>:</span> <span class=function>f</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>.</span><span class=property>val</span><span class=punctuation>),</span>
            <span class=property>next</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=property>next</span><span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span><span class=operator>&</span><span class=keyword>mut</span> f<span class=punctuation>),</span>
        <span class=punctuation>}</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span>
<span class=comment>/// Given all types T that can be mapped over, allow Option&lt;T> to be mapped over in the same way, automatically un/re-wrapping the Some case as required
</span><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>Output</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>Option</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span>
<span class=keyword>where</span>
    <span class=type>T</span><span class=punctuation>:</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>></span>
<span class=punctuation>{</span>
	<span class=comment>/// Input is the type given as input to the internal transform. In this case, because we are transparent, we use T's input
</span>    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>T</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>;</span>
    <span class=comment>/// Mapped is the external type after the transform has been done. Because we are wrapping T as input, we also wrap T's mapped type as output.
</span>    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>Option</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span>
        <span class=variable>self</span><span class=punctuation>,</span> <span class=comment>//: Option&lt;T></span>
        <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>T</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Output</span><span class=punctuation>,</span>
    <span class=punctuation>)</span> -> <span class=type>Option</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>></span> <span class=punctuation>{</span>
        <span class=type>Option</span><span class=punctuation>::</span><span class=function>map</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> |x| x<span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>))</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span>

<span class=comment>/// The implementation for Box&lt;T> is very similar to that for Option&lt;T>, included for completeness.
</span><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>Output</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>Box</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span>
<span class=keyword>where</span>
    <span class=type>T</span><span class=punctuation>:</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>>,</span>
<span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>T</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>Box</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span>
        <span class=variable>self</span><span class=punctuation>,</span>
        <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Output</span><span class=punctuation>,</span>
    <span class=punctuation>)</span> -> <span class=type>Box</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>></span> <span class=punctuation>{</span>
        <span class=type>Box</span><span class=punctuation>::</span><span class=function>new</span><span class=punctuation>((</span><span class=operator>*</span><span class=variable>self</span><span class=punctuation>).</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>))</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>Neat! This way, if we're writing a lot of transforms, they'll be composable with existing container types like <code>Option</code> & <code>Box</code>, as well as any other container types we might try.</p><p>However, I'm still a bit sad about having to write these transforms by hand. Especially given the fact that, while <code>Functor</code> is nice to implement for wrapper types like <code>Option</code> and <code>Box</code>, it isn't as nice for "AST-like" types...</p><h3><a aria-hidden=true class=anchor href=#problems-with-ast-like-types id=heading-problems-with-ast-like-types></a>Problems With AST-like Types</h3><p>Consider what would happen if we had the following set of items:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>A</span> <span class=punctuation>{</span>
    <span class=property>b</span><span class=punctuation>:</span> <span class=type>B</span><span class=punctuation>,</span>
<span class=punctuation>}</span>
<span class=keyword>struct</span> <span class=type>B</span> <span class=punctuation>{</span>
    <span class=property>c</span><span class=punctuation>:</span> <span class=type>C</span><span class=punctuation>,</span>
<span class=punctuation>}</span>
<span class=keyword>struct</span> <span class=type>C</span><span class=punctuation>(</span><span class=type>i32</span><span class=punctuation>);</span></code></pre><p>Naturally, we'd want to call <code>a.fmap(|c: C| ...)</code>. But! There is no item <code>C</code> in any direct fields of <code>A</code>, only indirectly thru <code>b: B</code>. If we only cared about this one <code>fmap</code> call, we could write:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>C</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>A</span> <span class=punctuation>{</span>
	<span class=keyword>type</span> <span class=type>Input</span> = <span class=type>C</span><span class=punctuation>;</span>
	<span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>A</span><span class=punctuation>;</span>
	<span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>C</span><span class=punctuation>)</span> -> <span class=type>C</span><span class=punctuation>)</span> -> <span class=type>A</span> <span class=punctuation>{</span>
		<span class=type>A</span> <span class=punctuation>{</span> <span class=property>b</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>)</span> <span class=punctuation>}</span>
	<span class=punctuation>}</span>
<span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>C</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>B</span> <span class=punctuation>{</span>
	<span class=keyword>type</span> <span class=type>Input</span> = <span class=type>C</span><span class=punctuation>;</span>
	<span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>B</span><span class=punctuation>;</span>
	<span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=keyword>mut</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>C</span><span class=punctuation>)</span> -> <span class=type>C</span><span class=punctuation>)</span> -> <span class=type>B</span> <span class=punctuation>{</span>
		<span class=type>B</span> <span class=punctuation>{</span> <span class=property>c</span><span class=punctuation>:</span> <span class=function>f</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>.</span><span class=property>c</span><span class=punctuation>)</span> <span class=punctuation>}</span>
	<span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>Which is fine. But, you might eventually add an item <code>D</code> that you also want to map over from <code>A</code>. Then you'd have to write:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>D</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>A</span> <span class=punctuation>{</span> ... <span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>D</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>B</span> <span class=punctuation>{</span> ... <span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>D</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>C</span> <span class=punctuation>{</span> ... <span class=punctuation>}</span></code></pre><p>Apparently, the number of <code>Functor</code> implementations you have to write scales linearly with both the depth of the tree <em>and</em> the number of things you want to map over, so quadratically in total, which is quite bad.</p><p>Ideally, you'd like to just scale linearly with the size of the tree. Here's one approach for this:</p><pre class=syntax-highlighting><code class=language-rust><span class=comment>/// These "base impls" can all be easily generated with a macro
</span><span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>A</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>A</span> <span class=punctuation>{</span>
	<span class=keyword>type</span> <span class=type>Input</span> = <span class=type>A</span><span class=punctuation>;</span>
	<span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>A</span><span class=punctuation>;</span>
	<span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=keyword>mut</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>A</span><span class=punctuation>)</span> -> <span class=type>A</span><span class=punctuation>)</span> -> <span class=type>A</span> <span class=punctuation>{</span>
		<span class=function>f</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>)</span>
	<span class=punctuation>}</span>
<span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>B</span> <span class=punctuation>{</span> ... <span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>C</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>C</span> <span class=punctuation>{</span> ... <span class=punctuation>}</span>

<span class=comment>/// These "recursive impl"s are written manually, but that's OK, we only need one per item!
</span><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>A</span> <span class=keyword>where</span> <span class=type>B</span><span class=punctuation>:</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>Mapped</span>=<span class=type>B</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = &lt;<span class=type>B</span> <span class=keyword>as</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span>><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>A</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>T</span><span class=punctuation>)</span> -> <span class=type>A</span> <span class=punctuation>{</span>
        <span class=type>Self</span> <span class=punctuation>{</span>
            <span class=property>b</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=property>b</span><span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>),</span>
        <span class=punctuation>}</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span>
<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>B</span> <span class=keyword>where</span> <span class=type>C</span><span class=punctuation>:</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>Mapped</span>=<span class=type>C</span><span class=punctuation>></span> <span class=punctuation>{</span> ... <span class=punctuation>}</span></code></pre><p>Seasoned Rust veterans may be able to spot the problem: <code>impl&lt;A> Functor&lt;A> for A</code> and <code>impl&lt;T> Functor&lt;T> for A</code> conflict! Never mind that <code>where</code> clause, the Rust compiler is sometimes very particular about preventing future conflicts too<sup class=footnote-ref><a data-footnote-ref href=#fn-conflicts id=fnref-conflicts>2</a></sup>, just in case <code>B: Functor&lt;A, Mapped=B></code> somehow.</p><p>If only there were a way to automatically write all those quadratic dumb-but-non-conflicting implementations... then it wouldn't matter that there's so many...<sup class=footnote-ref><a data-footnote-ref href=#fn-compiletimes id=fnref-compiletimes>3</a></sup></p><h2><a aria-hidden=true class=anchor href=#approach-3-use-a-proc_macro id=heading-approach-3-use-a-proc_macro></a>Approach 3: Use A <code>proc_macro</code></h2><p>Turns out, there's enough information in the item definitions alone to generate the traversals automatically. All we really need is:</p><ol><li>A list of all items considered a part of the AST</li><li>The type of each field in each item</li><li>A way of determining valid transforms to generate for each item</li><li>A way of generating each valid transform</li></ol><p>We need (1) because of the problem presented before: it's not possible to generate <code>Functor</code> impls for <em>arbitrary</em> types, so we must explicitly choose the ones we want to map over.</p><p>(2) and (4) are also fairly straightforward given Rust's de-facto introspection libraries, <a href=https://docs.rs/syn/latest/syn/>syn</a> & <a href=https://docs.rs/quote/latest/quote/>quote</a><sup class=footnote-ref><a data-footnote-ref href=#fn-dtolnay id=fnref-dtolnay>4</a></sup>. There are some subtleties when it comes to actually extracting the information we want (the Rust type grammar is so much...), but overall "get the type of a field" and "get the shape of an item" is not too hard. Same with generating the transform; once you have the information of "what type am I transforming" and "what fields should I transform for that type", emitting the code is fairly straightforward. You can <a href=https://github.com/p0lyw0lf/pwcc/blob/715eca08fc3e6acc5ba65030d096bf383c78c054/functional_macros/src/traits/functor.rs>take a look at my code</a> if you're curious, but I promise it's really boring.</p><p>(3), on the other hand, is much more difficult + interesting, and is a big reason I kept delaying this piece. Turns out re-creating the Rust trait implementation coherence semantics is pretty involved!!</p><h3><a aria-hidden=true class=anchor href=#what-even-is-a-valid-transformation id=heading-what-even-is-a-valid-transformation></a>What Even Is A Valid Transformation?</h3><p>Let's start out with the most basic criteria. Any valid implementation of <code>Functor&lt;B> for A</code> comes about because:</p><ol><li><code>A</code> has a field with type <code>B</code>, where "with type" looks inside any wrapper types like <code>Option</code>/<code>Box</code>, OR</li><li><code>A</code> has a field with type <code>C</code>, where <code>Functor&lt;B> for C</code> is a valid transform.</li></ol><p>We get the information for (1) when reading in the items initially. To get (2), we must turn to the horror that is graph algorithms in Rust. Get ready for some math!</p><h3><a aria-hidden=true class=anchor href=#1-the-lattice-step id=heading-1-the-lattice-step></a>1: The Lattice Step</h3><p>I call this a "lattice" because it encodes a "higher-than" relationship that's useful for what I'm doing. It's not a <em>true</em> lattice by many definitions of the word, but im the programmer so i get to name things badly ok</p><p>What this step looks like is, taking all edges matching <code>Functor&lt;C> for A</code> (denoted as <code>A -> C</code>) + <code>C -> B</code>, and collecting them into a new edge <code>A -> B</code>. This is so we can get all the possible implementations that would be covered by case (2) above. There are a few subtleties when it comes to unifying generic contexts, but like the mathematician I'm cosplaying as, I'll leave those for the appendix<sup class=footnote-ref><a data-footnote-ref href=#fn-unifying id=fnref-unifying>5</a></sup>.</p><p>However! We're not done yet. Creating all these new edges gives us all the <em>possible</em> implementations, but not all the <em>valid</em> ones. To do this, we need to filter out all invalid edges, so let's look at some ways an implementation (as defined by an edge) can be invalid.</p><h3><a aria-hidden=true class=anchor href=#2-filter-out-conflicting-generic-instantiations id=heading-2-filter-out-conflicting-generic-instantiations></a>2: Filter Out Conflicting Generic Instantiations</h3><p>Consider the following:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>A1</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=property>bt</span><span class=punctuation>:</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>,</span>
    <span class=property>bu</span><span class=punctuation>:</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>,</span>
<span class=punctuation>}</span>
<span class=keyword>struct</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>(</span><span class=type>T</span><span class=punctuation>);</span></code></pre><p>If we try list out all the possible implementations, we get:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>></span> <span class=keyword>for</span> <span class=type>A1</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=punctuation>{</span> ... <span class=punctuation>}</span>
<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>></span> <span class=keyword>for</span> <span class=type>A1</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=punctuation>{</span> ... <span class=punctuation>}</span></code></pre><p>Note we have two implementations, because <code>B&lt;T></code> and <code>B&lt;U></code> are different types. But clearly, these implementations conflict! This is because <code>Functor&lt;B&lt;T>></code> and <code>Functor&lt;B&lt;U>></code> are <em>not</em> different traits. Without any preference for which implementation to keep, we discard both.</p><p>There is a similar conflict if we instead have:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>A2</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=property>bt</span><span class=punctuation>:</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>,</span>
    <span class=property>bi</span><span class=punctuation>:</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>i32</span><span class=punctuation>>,</span>
<span class=punctuation>}</span></code></pre><p>Because <code>Functor&lt;B&lt;T>></code> <em>could</em> be the same trait as <code>Functor&lt;B&lt;i32>></code>, and without a "type inequality" bound (not possible to express in Rust iirc), we can't generate these. In this case, we just prefer the <code>Functor&lt;B&lt;i32>></code> implementation that only transforms <code>bi</code>, because without generic parameters, implementations can never conflict.</p><p>The algorithm for deciding if a pair of instantiations conflict is as follows:</p><ol><li>For each pair of arguments in order: <ol><li>If both are generic, they must be the same generic. Otherwise, there's a conflict.</li><li>If only one is generic, there's a conflict</li><li>If neither are generic, there's no conflict.</li></ol></li></ol><h3><a aria-hidden=true class=anchor href=#3-restrict-which-generic-parameters-are-transformed id=heading-3-restrict-which-generic-parameters-are-transformed></a>3: Restrict Which Generic Parameters Are Transformed</h3><p>This is a bit of a case of me being burned by making my <code>Functor</code> too complex, but screw it I <em>did</em> want that complexity. The situation is, if you have something like:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>A</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=property>b</span><span class=punctuation>:</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>,</span>
    <span class=property>c</span><span class=punctuation>:</span> <span class=type>C</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>,</span>
<span class=punctuation>}</span>
<span class=keyword>struct</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=property>c</span><span class=punctuation>:</span> <span class=type>C</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>,</span>
<span class=punctuation>}</span>
<span class=keyword>struct</span> <span class=type>C</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>(</span><span class=type>T</span><span class=punctuation>);</span></code></pre><p>And we try to write the "full" implementation for <code>A -> B</code> that transforms its generic parameter:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>></span> <span class=keyword>for</span> <span class=type>A</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>A</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>)</span> -> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>)</span> -> <span class=type>A</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>></span> <span class=punctuation>{</span>
        <span class=type>A</span> <span class=punctuation>{</span>
            <span class=property>b</span><span class=punctuation>:</span> <span class=function>f</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>.</span><span class=property>b</span><span class=punctuation>),</span>
            <span class=property>c</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=property>c</span><span class=punctuation>,</span> <span class=comment>// ERROR: expected C&lt;U>, got C&lt;T></span>
        <span class=punctuation>}</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>Oh no type error!! The reason it happens is because we can't transform the generic type of <code>c</code> even though we need to. There are a couple parts to this:</p><ol><li>How do we know we need to transform <code>c</code>? <ul><li>Because the generic context used by field <code>b</code>, and therefore the generic context transformed by <code>b</code>, is also used by field <code>c</code>.</li></ul></li><li>How do we know we cannot transform <code>c</code>? <ul><li>We look at all the outgoing edges from item <code>C</code>, and if there are none that would satisfy the transformation <code>C -> B</code> (for the same instantiated item <code>B</code> as is being considered for field <code>b</code>), then field <code>c</code> cannot be transformed.</li></ul></li></ol><p>Anyways, this is all just to check if we can transform the generic parameter <code>T</code> to <code>U</code>. If we can't, no worries, we can just generate a <code>Functor</code> implementation that doesn't transform <code>T</code> :) Like any sane person would do :)</p><h3><a aria-hidden=true class=anchor href=#what-is-the-correct-order-for-these-steps id=heading-what-is-the-correct-order-for-these-steps></a>What Is The Correct Order For These Steps?</h3><p>If you've been paying attention (no worries if not, it's a long one, you're almost there!), you may have noticed something: step 3 above look at outgoing edges for all nodes one level "below" the current. However, step 2 explicitly removes some edges from the graph. So! We must be smart about the order in which we look at nodes for step 3, so we don't end up keeping an edge <code>A -> B</code> only to later remove the edge <code>C -> B</code> that made keeping it possible.</p><p>I claim the correct traversal order is "reverse <a href=https://en.wikipedia.org/wiki/Topological_sorting>topological sort</a> order". (Whoopee, more graph algorithms in Rust!) Basically, we should start with nodes that don't point to anything (the "bottom"), then nodes that only point to previously explored nodes, and so on. This ensures we don't end up in that bad scenario.</p><p>However! The topological sort order is only defined for directed graphs with no loops. Our graph <em>can</em> have loops, and indeed, that's a big feature for this macro in general, being able to support trees & linked lists & such, which all have edges that look like <code>A -> A</code> or even <code>A -> B -> C -> A</code>. Are we out of luck?</p><p>Not yet! By first finding the <a href=https://en.wikipedia.org/wiki/Strongly_connected_component>strongly connected components</a> (omg more graph algorithms!!), we can do a topological sort on <em>those</em>, which are guaranteed to not have loops between them, and our traversal order will be good.</p><p>"But what about the order within a strongly connected component?", you may ask. Well, thanks to our lattice step earlier, every loop <code>A -> B -> C -> A</code> in the graph will become a clique, so if <code>A</code>, <code>B</code>, and <code>C</code> are in the same strongly connected component, <code>A -> B</code> & <code>C -> B</code> will always exist. Wow it's almost like I'm actually doing math!!</p><p>So, the correct order of steps is:</p><ol><li>Lattice step</li><li>Filter out conflicting instantiations</li><li>Topological sort</li><li>Restrict generic parameters</li></ol><p>And that's all there is to it! ;)</p><h2><a aria-hidden=true class=anchor href=#conclusion id=heading-conclusion></a>Conclusion</h2><p>At long last, we have reached the end of everything I know about automatically deriving a <code>Functor</code> implementation. These rules have worked out for me in practice, for <a href=https://github.com/p0lyw0lf/pwcc/blob/715eca08fc3e6acc5ba65030d096bf383c78c054/pwcc/src/parser.rs#L24-L164>two very</a> <a href=https://github.com/p0lyw0lf/pwcc/blob/715eca08fc3e6acc5ba65030d096bf383c78c054/pwcc/src/codegen.rs#L19-L111>large ASTs</a>. There are more things I know, like deriving <a href=https://github.com/p0lyw0lf/pwcc/blob/715eca08fc3e6acc5ba65030d096bf383c78c054/functional/src/try_functor.rs#L8-L25>TryFunctor</a> and <a href=https://github.com/p0lyw0lf/pwcc/blob/715eca08fc3e6acc5ba65030d096bf383c78c054/functional/src/foldable.rs#L5-L22>Foldable</a> and <a href=https://github.com/p0lyw0lf/pwcc/blob/715eca08fc3e6acc5ba65030d096bf383c78c054/functional/src/test_visit.rs#L7-L50>Visit</a>, but those all use these same basic principles too. There are also many more things I do not know, like "what if a field contains a generic type but doesn't have one of the designated items?" or "how can I prevent mapping invalid field types, like <code>fn(T) -> U</code>" or "am I using <code>panic!</code> to handle errors too often", but those are all left for later/never. I do not recommend anyone else attempt to use this code btw, it is simply a dangerous toy for my own amusement, and it <em>will</em> blow up in ur face.</p><p>Anyways, thanks for reading, see u next time!! :3</p><section class=footnotes data-footnotes><ol><li id=fn-functor><p>If you look in <a href=https://github.com/p0lyw0lf/pwcc/blob/d40f72bfd97c8b95bda9e12ec9637c1d4d6fe56c/functional/src/functor.rs#L21-L25>my repository</a>, you'll see I actually use a couple modifications: (1) it takes in an <code>&mut impl FnMut</code>, and (2) it takes in an extra <code>RecursiveCall</code> parameter. These are for structures like binary trees, where (1) the same <code>f</code> can't be moved into both calls, and trying to <code>&mut</code> it generates an infinite sequence of <code>&mut</code> checks, and (2) controlling when <code>f</code> would be called on a parent node: before its children, after its children, or not calling into children at all. Anyways! Not important for this piece, all the other relevant details are the same. <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-functor>↩</a></p></li><li id=fn-conflicts><p>As it turns out, if all of these implementations & items are in the same crate as <code>Functor</code>, with private visibility, this actually works out! (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=022e8efdd57f9085dd0729093a64f046">playground link</a>.) The compiler can prove that the implementations never conflict, and these will be the only implementations that exist, so everything is groovy. Unfortunately, I do define my <code>Functor</code> trait in a separate crate from my AST items, for pretty obvious separation-of-concern reasons, so everything I've said still stands. <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-conflicts>↩</a></p></li><li id=fn-compiletimes><p>Except for compile times, of course :P It did eventually get so bad in my project that I had to make "being <code>Input</code>" opt-in, reducing hot-rebuild times from 30s to 5s. <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-compiletimes>↩</a></p></li><li id=fn-dtolnay><p>I should really get off these... dtolnay did my boy JeanHeyd real dirty, introwospection in its full form would have been perfect for this project, but alas. If I can stomach runtime reflection instead of compile-time reflection (which might happen as soon as I take a real look at binary sizes), I should consider using <a href=https://docs.rs/facet-reflect/latest/facet_reflect/>facet-reflect</a> instead... <a aria-label="Back to reference 4" class=footnote-backref data-footnote-backref data-footnote-backref-idx=4 href=#fnref-dtolnay>↩</a></p></li><li id=fn-unifying><p>Consider a struct declared as <code>A&lt;T></code> with field type <code>B&lt;T></code>, and a struct declared as <code>B&lt;U></code> with field type <code>C&lt;U></code>. When generating the edge <code>A -> C</code>, we want to turn the <code>C&lt;U></code> declaration into a <code>C&lt;T></code> one. You have to lookup the variables in the generic context to do the mapping appropriately, neat stuff! <a aria-label="Back to reference 5" class=footnote-backref data-footnote-backref data-footnote-backref-idx=5 href=#fnref-unifying>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Quick Thoughts On: React vs SolidJS]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2025-03-20-quick-thoughts-on-react-vs-solidjs/" />
      <id>https://wolfgirl.dev/blog/2025-03-20-quick-thoughts-on-react-vs-solidjs/</id>
      <published>2025-03-20T16:56:15Z</published>
      <updated>2025-03-20T16:56:15Z</updated>
      <summary><![CDATA[TL;DR just use React, or Preact if you care about bundle size.  ---  A while ago, I [wrote my own post composer](https://wolfgirl.dev...]]></summary>
      <content type="html"><![CDATA[<p>TL;DR just use React, or Preact if you care about bundle size.</p><hr><p>A while ago, I <a href=https://wolfgirl.dev/blog/2024-11-08-what-if-anyone-could-use-my-post-composer-/>wrote my own post composer</a>, which I use to make blog posts from my phone, including this one. It's pretty nice!</p><p>One of the requirements I had when making it was "I want my bundle sizes to be as small as possible". Inspired by seeing my work's 66MB React app, I decided to look for other frontend frameworks, still reactive (because that <em>is</em> just the best paradigm), but smaller & faster.</p><p>Eventually, I settled on SolidJS. After having used it for a while on more than just a simple "Hello, World!" project, I can confidently claim there's a pretty fatal (for me) flaw:</p><h2><a aria-hidden=true class=anchor href=#solidjs-defaults-to-not-re-rendering-when-values-change id=heading-solidjs-defaults-to-not-re-rendering-when-values-change></a>SolidJS defaults to <em>not</em> re-rendering when values change</h2><p>From my understanding, this is how SolidJS works: everything is based around the "signal" as the underlying reactive primitive, with function calls as the way to subscribe to those signals:</p><pre class=syntax-highlighting><code class=language-jsx><span class=keyword>export</span> <span class=keyword>const</span> <span class=constructor>Component</span> <span class=operator>=</span> <span class=punctuation>(</span><span class=variable>props</span><span class=punctuation>)</span> <span class=operator>=></span> <span class=punctuation>{</span>
  <span class=keyword>const</span> <span class=punctuation>[</span><span class=variable>name</span><span class=punctuation>,</span> <span class=variable>setName</span><span class=punctuation>]</span> <span class=operator>=</span> <span class=function>createSignal</span><span class=punctuation>(</span><span class=string>""</span><span class=punctuation>);</span>
  <span class=keyword>const</span> <span class=function>title</span> <span class=operator>=</span> <span class=punctuation>()</span> <span class=operator>=></span> <span class=string>`</span><span class=punctuation>${</span><span class=variable>props</span><span class=punctuation>.</span><span class=property>greeting</span><span class=punctuation>}</span><span class=string>, </span><span class=punctuation>${</span><span class=function>name</span><span class=punctuation>()}</span><span class=string>`</span><span class=punctuation>;</span>
  <span class=keyword>return</span> <span class=punctuation>(</span>
    <span class=operator>&lt;</span><span class=variable>form</span><span class=operator>></span>
      <span class=operator>&lt;</span><span class=constructor>Title</span> <span class=property>text</span><span class=operator>=</span><span class=punctuation>{</span><span class=function>title</span><span class=punctuation>()}</span> />
      <span class=operator>&lt;</span><span class=variable>input</span> <span class=property>type</span><span class=operator>=</span><span class=string>"text"</span> <span class=property>value</span><span class=operator>=</span><span class=punctuation>{</span><span class=function>name</span><span class=punctuation>()}</span> <span class=property>onChange</span><span class=operator>=</span><span class=punctuation>{</span><span class=variable>e</span> <span class=operator>=></span> <span class=function>setName</span><span class=punctuation>(</span><span class=variable>e</span><span class=punctuation>.</span><span class=property>target</span><span class=punctuation>.</span><span class=property>value</span><span class=punctuation>)}</span> />
    &lt;/<span class=variable>form</span><span class=operator>></span>
  <span class=punctuation>);</span>
<span class=punctuation>}</span></code></pre><p>When <code>title()</code> is called, it will then call both <code>name()</code> (obviously) and <code>props.greeting</code> (non-obviously, since it's a special getter, which is why <a href=https://docs.solidjs.com/concepts/components/props#destructuring-props>you can't destructure <code>props</code></a>). By calling these functions, anything that calls <code>title()</code> will then know to re-render whenever either <code>name()</code> or <code>props.greeting</code> changes. So, <code>&lt;Title /></code> will re-render when <code>props.greeting</code> changes, and both <code>&lt;Title /></code> and <code>&lt;input></code> will re-render when <code>name()</code> changes.</p><p>However, if you <em>don't</em> make <code>title</code> a function, then <code>name()</code> and <code>props.greeting</code> will be called "too early", and <code>&lt;Title /></code> will never re-render; the body of <code>Component</code> isn't hooked up to any reactivity, just special places like the return value & <code>createEffect</code>.</p><p>This whole approach makes fine-grained reactivity quite simple, but also makes actually writing components a bit complex. You have to hook up all the reactive parts yourself!! It's very easy to get this wrong (I did many times), and the tooling (eslint) isn't helpful at detecting when it happens.</p><h2><a aria-hidden=true class=anchor href=#react-may-over-render-but-at-least-its-easier-to-conceptualize id=heading-react-may-over-render-but-at-least-its-easier-to-conceptualize></a>React may over-render, but at least it's easier to conceptualize</h2><p>Here's the equivalent React example; it's just minor syntactical differences:</p><pre class=syntax-highlighting><code class=language-jsx><span class=keyword>export</span> <span class=keyword>const</span> <span class=constructor>Component</span> <span class=operator>=</span> <span class=punctuation>({</span> greeting <span class=punctuation>})</span> <span class=operator>=></span> <span class=punctuation>{</span>
  <span class=keyword>const</span> <span class=punctuation>[</span><span class=variable>name</span><span class=punctuation>,</span> <span class=variable>setName</span><span class=punctuation>]</span> <span class=operator>=</span> <span class=function>useState</span><span class=punctuation>(</span><span class=string>""</span><span class=punctuation>);</span>
  <span class=keyword>const</span> <span class=variable>title</span> <span class=operator>=</span> <span class=string>`</span><span class=punctuation>${</span><span class=variable>greeting</span><span class=punctuation>}</span><span class=string>, </span><span class=punctuation>${</span><span class=variable>name</span><span class=punctuation>}</span><span class=string>`</span><span class=punctuation>;</span>
  <span class=keyword>return</span> <span class=punctuation>(</span>
    <span class=operator>&lt;</span><span class=variable>form</span><span class=operator>></span>
      <span class=operator>&lt;</span><span class=constructor>Title</span> <span class=property>text</span><span class=operator>=</span><span class=punctuation>{</span><span class=variable>title</span><span class=punctuation>}</span> />
      <span class=operator>&lt;</span><span class=variable>input</span> <span class=property>type</span><span class=operator>=</span><span class=string>"text"</span> <span class=property>value</span><span class=operator>=</span><span class=punctuation>{</span><span class=variable>name</span><span class=punctuation>}</span> <span class=property>onChange</span><span class=operator>=</span><span class=punctuation>{</span><span class=variable>e</span> <span class=operator>=></span> <span class=function>setName</span><span class=punctuation>(</span><span class=variable>e</span><span class=punctuation>.</span><span class=property>target</span><span class=punctuation>.</span><span class=property>value</span><span class=punctuation>)}</span> />
    &lt;/<span class=variable>form</span><span class=operator>></span>
  <span class=punctuation>);</span>
<span class=punctuation>}</span></code></pre><p>How React works is, any time the component props/<code>useState</code> values change, it runs the component body again, as well as any children, recursively. This gets us a lot of neat benefits over SolidJS, like:</p><ul><li>you can destructure <code>props</code></li><li>not everything has to be functions</li><li>if something changes in state, it is guaranteed to cause a re-render that will display the change.</li></ul><p>That approach would be expensive if, when a re-render happened, we had to remove all the old HTML elements from the page and add back the new ones, so instead React uses something called "Virtual DOM Diffing" to only call browser APIs for elements/attributes that need changing. Some unavoidable downsides are the fact you need to keep a virtual copy of the whole tree in-memory, as well as the pretty complex algorithm to do the diffing.</p><p>SolidJS cuts thru all this implementation complexity & potential runtime wastefulness by composing a simple + powerful primitive, signals. Unfortunately, it's a bit <em>too</em> simple + powerful, ending up finnicky to use as a result, like a Lisp, perhaps.</p><p>If you're making a non-trivial Javascript-powered application, just use React. The <a href=https://react.dev/learn/react-compiler>React Compiler</a> takes care of a lot of the problems React has, effectively providing fine-grained reactivity "for free" (bit more compile-time cost oop). <a href=https://preactjs.com/>Preact</a> is also available if you care about bundle sizes; the only reason I didn't choose it initially was because I wanted to learn a new framework. Still, sometimes u just gotta go back to ol' reliable.</p>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Optimizing Markdown Images With Astro]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2025-02-12-optimizing-markdown-images-with-astro/" />
      <id>https://wolfgirl.dev/blog/2025-02-12-optimizing-markdown-images-with-astro/</id>
      <published>2025-02-13T03:36:35Z</published>
      <updated>2025-02-13T03:36:35Z</updated>
      <summary><![CDATA[I dug into this because it had been on my mind for a while, and I wanted to get something working before I uploaded Japan trip photos. A...]]></summary>
      <content type="html"><![CDATA[<p>A previous version of this post contained some factual errors. I'm re-publishing this under a new URL so that it will update in your RSS reader. Updates are noted where applicable.</p><hr><p>I dug into this because it had been on my mind for a while, and I wanted to get something working before I uploaded Japan trip photos. A couple nights later, I have learned a bit about how Astro & Remark/Rehype & Vite work, and a lot about how what I'm trying to do is futile.</p><p>From my understanding, this is how Astro optimizes <a href=https://docs.astro.build/en/guides/images/#images-in-markdown-files>local images in Markdown files</a>:</p><ol><li><a href=https://github.com/withastro/astro/blob/8d4e566f5420c8a5406e1e40e8bae1c1f87cbe37/packages/markdown/remark/src/remark-collect-images.ts>remark-collect-images.ts</a>, a Markdown AST transformation step, finds all the image paths that look like <code>![]()</code> and not <code>&lt;img src="" /></code>, so we only optimize the Markdown images. It records this information into context that's passed to the next step.</li><li><a href=https://github.com/withastro/astro/blob/8d4e566f5420c8a5406e1e40e8bae1c1f87cbe37/packages/markdown/remark/src/rehype-images.ts>rehype-images.ts</a>, an HTML AST transform step, turns all those images that look like <code>&lt;img src="./local.png" /></code><sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>1</a></sup> and turns them into something that looks like <code>&lt;img __ASTRO_IMAGE_="{&amp;#x22;src&amp;#x22;:&amp;#x22;./local.png&amp;#x22;,&amp;#x22;index&amp;#x22;:0}" /></code>. It does this because it's easier to look for, and safer to parse later compared to raw HTML.</li><li><a href=https://github.com/withastro/astro/blob/8d4e566f5420c8a5406e1e40e8bae1c1f87cbe37/packages/astro/src/vite-plugin-markdown/images.ts>vite-plugin-markdown/images.ts</a> takes the list of local image URLs (generated as part of transforming the Markdown into HTML in the above steps), and the HTML with these <code>__ASTRO_IMAGE_</code> things in them, and generates a full-on Astro component, Javascript source & everything, to be rendered later.</li></ol><p>Phew, that's a lot!! Hope you're still following along :)</p><p>So, the procedure I described works perfectly fine for standalone <code>.md</code> pages. The problem comes when trying to apply this same transformation to markdown files rendered as part of a <a href=https://docs.astro.build/en/reference/modules/astro-content/>content collection</a>, which is what my blog files are stored as. Instead of being rendered as part of the "main" Astro flow, content collection entries are rendered separately, as part of a loader, like <a href=https://github.com/withastro/astro/blob/8d4e566f5420c8a5406e1e40e8bae1c1f87cbe37/packages/astro/src/content/loaders/glob.ts#L183>content/loaders/glob.ts</a>. For Markdown, this calls into <a href=https://github.com/withastro/astro/blob/8d4e566f5420c8a5406e1e40e8bae1c1f87cbe37/packages/astro/src/vite-plugin-markdown/content-entry-type.ts>vite-plugin-markdown/content-entry-type.ts</a>, which does no such image replacement, because content entries aren't in the correct stage of the build.</p><p><del>And I don't think we <em>can</em> get to the correct stage here! The whole reason this code does the <code>__ASTRO_IMAGE_</code> rigamarole is so it can call the image transformation function at the correct time. With content collections, all the rendering happens "too early", so the images can't be transformed.</del> UPDATE 2025-02-15: This is incorrect. The replacement happens in <a href=https://github.com/withastro/astro/blob/3842ce5ec9471d358042b3d9ef697cf06c7a91f6/packages/astro/src/content/runtime.ts#L401-L447>content/runtime.ts</a>, during the actual rendering, which makes sense. I wasn't seeing this before because (a) im bad at looking and (b) it's gated on Astro detecting images in the body, which it does not currently do for remote images.</p><p>Alternatively, I <em>could</em> use <a href=https://docs.astro.build/en/guides/integrations-guide/mdx/>MDX</a>, which is like Markdown but compiles to Javascript instead of HTML, which <em>does</em> always have the image transformation step, thanks to <a href=https://github.com/withastro/astro/blob/8d4e566f5420c8a5406e1e40e8bae1c1f87cbe37/packages/integrations/mdx/src/rehype-images-to-component.ts>rehype-image-to-component.ts</a>. However, I do not like MDX's weird character restrictions (c'mon, no <code>&lt;</code>, really??), so I will not use it >:(</p><p>At this point, I'm seriously considering moving away from Astro's built-in image optimization service to my own... For now though, I'm giving up 😔</p><p>UPDATE 2025-02-13: I have just discovered that <a href=https://docs.astro.build/en/guides/integrations-guide/markdoc/#custom-image-components>Markdoc</a> does exactly what I want it to: provide the ability to use a custom component to override what <code>![]()</code> means, without limiting Markdown syntax. This is very very good news! Here's hoping those files will still have reasonable build times. Also I should probably make it so my Post composer re-uses the Markdoc config...</p><p>UPDATE 2024-02-14: Markdoc also doesn't work for my needs because it doesn't have footnote support :((((( I make heavy use of footnotes in my text and I do not want to change how I write those. The whole <em>point</em> of Markdown is to make it so you don't look like you're formatting your text with tags, and Markdoc ignores that and immediately goes back to demanding you use tags for nonstandard features. It seems I am better served by the remark/rehype ecosystem for my parsing needs, so I will continue to do that.</p><p>UPDATE 2024-02-15: I did it! I fixed Astro!! The above information was a tad incorrect, what I wanted to do was possible with a bit of elbow grease, so I put in the work and opened a couple PRs: <a href=https://github.com/withastro/astro/pull/13254>#13254</a> & <a href=https://github.com/withastro/astro/pull/13256>#13256</a>. I also have the necessary patches for <a href=https://github.com/p0lyw0lf/website/blob/d7a866fef2356fb194527fc7cf48c8f20e616cc6/patches/astro.patch>astro</a> and <a href=https://github.com/p0lyw0lf/website/blob/d7a866fef2356fb194527fc7cf48c8f20e616cc6/patches/%40astrojs__markdown-remark.patch>@astrojs/markdown-remark</a> in my website directly, so I don't have to wait for them to be upstreamed.</p><section class=footnotes data-footnotes><ol><li id=fn-1><p>I believe this is a minor bug: if you have both <code>![](./local.png)</code> <em>and</em> <code>&lt;img src="./local.png" /></code>, I think the latter image will be transformed when it shouldn't be. At least, that's if I'm reading the code right, which I may well not be :P <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-1>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[One Neat Trick For Writing Large `macro_rules!`]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2025-01-17-one-neat-trick-for-writing-large-macro-rules-/" />
      <id>https://wolfgirl.dev/blog/2025-01-17-one-neat-trick-for-writing-large-macro-rules-/</id>
      <published>2025-01-18T01:18:03Z</published>
      <updated>2025-01-18T01:18:03Z</updated>
      <summary><![CDATA[First, let's start with a related "neat trick" that lets you write _composable_ `macro_rules!`. Say you have a macro that generates struc...]]></summary>
      <content type="html"><![CDATA[<p>First, let's start with a related "neat trick" that lets you write <em>composable</em> <code>macro_rules!</code>. Say you have a macro that generates struct definitions<sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>1</a></sup>:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>macro_rules</span>! node <span class=punctuation>{</span>
    <span class=punctuation>(</span>
        $name<span class=punctuation>:</span>ident <span class=punctuation>[</span> $subnode<span class=punctuation>:</span>ident <span class=punctuation>]</span>
    <span class=punctuation>)</span> => <span class=punctuation>{</span>
        <span class=keyword>pub</span> <span class=keyword>struct</span> $name<span class=punctuation>(</span><span class=constructor>Vec</span><span class=punctuation>&lt;</span>$subnode<span class=punctuation>>);</span>
    <span class=punctuation>};</span>
    <span class=punctuation>(</span>
        $name<span class=punctuation>:</span>ident <span class=punctuation>(</span> $<span class=punctuation>(</span>
            <span class=operator>*</span> $field<span class=punctuation>:</span>ident <span class=punctuation>:</span> $ty<span class=punctuation>:</span>ty
        <span class=punctuation>)</span>+ <span class=punctuation>)</span>
    <span class=punctuation>)</span> => <span class=punctuation>{</span>
        <span class=keyword>pub</span> <span class=keyword>struct</span> $name <span class=punctuation>{</span> $<span class=punctuation>(</span>
            $field<span class=punctuation>:</span> $ty<span class=punctuation>,</span>
        <span class=punctuation>)</span>+ <span class=punctuation>}</span>
    <span class=punctuation>};</span>
    <span class=punctuation>(</span>
        $name<span class=punctuation>:</span>ident <span class=punctuation>(</span> $<span class=punctuation>(</span>
            + $ty<span class=punctuation>:</span>ident
        <span class=punctuation>)</span>+ <span class=punctuation>)</span>
    <span class=punctuation>)</span> => <span class=punctuation>{</span>
        <span class=keyword>pub</span> <span class=keyword>enum</span> $name <span class=punctuation>{</span> $<span class=punctuation>(</span>
            $ty<span class=punctuation>(</span>$ty<span class=punctuation>),</span>
        <span class=punctuation>)</span>+ <span class=punctuation>}</span>
    <span class=punctuation>};</span>
<span class=punctuation>}</span></code></pre><p>Then, what you might want to do next is generate all these all in one macro invocation, so you don't have to write <code>node!()</code> over and over again. You might think, "darn, looks like I'll have to write all these exact branches again in order to capture them in an outer macro". But fear not, weary traveler! For I bear great news:</p><blockquote><p>Neat Trick 1: Use <code>tt</code> to capture arguments that you pass to other macro invocations.</p></blockquote><pre class=syntax-highlighting><code class=language-rust><span class=keyword>macro_rules</span>! nodes <span class=punctuation>{</span>
    <span class=punctuation>(</span> $<span class=punctuation>(</span>
        $name<span class=punctuation>:</span>ident $tt<span class=punctuation>:</span>tt <span class=punctuation>;</span>
    <span class=punctuation>)</span><span class=operator>*</span> <span class=punctuation>)</span> => <span class=punctuation>{</span>
        $<span class=punctuation>(</span>
            <span class=macro>node!</span><span class=punctuation>(</span>$name $tt<span class=punctuation>);</span>
        <span class=punctuation>)</span><span class=operator>*</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p><code>tt</code>, or TokenTree, represents a pair of braces (round <code>()</code>, square <code>[]</code>, or curly <code>{}</code>)<sup class=footnote-ref><a data-footnote-ref href=#fn-2 id=fnref-2>2</a></sup>, with <em>anything</em> inside them. Rust is really good about enforcing braces to be in pairs even at the lex level, and this is represented in macros, which <em>do</em> just take in an arbitrary list of tokens.</p><p>So now we have a macro that we can use like this:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>mod</span> ast <span class=punctuation>{</span>
    <span class=macro>nodes!</span><span class=punctuation>(</span>
        <span class=constructor>Program</span><span class=punctuation>[</span><span class=constructor>Function</span><span class=punctuation>];</span>
        <span class=function>Function</span><span class=punctuation>(</span>
            <span class=operator>*</span>open_brace<span class=punctuation>:</span> <span class=constructor>OpenBrace</span>
            <span class=operator>*</span>body<span class=punctuation>:</span> <span class=constructor>Body</span>
            <span class=operator>*</span>close_brace<span class=punctuation>:</span> <span class=constructor>CloseBrace</span>
        <span class=punctuation>);</span>
        <span class=constructor>Body</span><span class=punctuation>[</span><span class=constructor>BlockItem</span><span class=punctuation>];</span>
        <span class=function>BlockItem</span><span class=punctuation>(</span> +<span class=constructor>Statement</span> +<span class=constructor>Declaration</span> <span class=punctuation>);</span>
        <span class=comment>// ...</span>
    <span class=punctuation>);</span>
<span class=punctuation>}</span></code></pre><p>But what if that's not good enough? What if we need <strong><em>more</em></strong>?</p><h2><a aria-hidden=true class=anchor href=#more id=heading-more></a>More?</h2><p>To keep things short: in my <a href=https://github.com/p0lyw0lf/pwcc>compiler project</a>, I have a proc_macro that wants to read in a whole bunch of <code>struct</code>/<code>enum</code> definitions at once, by being an attribute on a module. However! There is a dilemma: the simplest approach doesn't work.</p><pre class=syntax-highlighting><code class=language-rust><span class=attribute>#</span><span class=punctuation>[</span><span class=attribute>my_proc_macro</span><span class=punctuation>]</span>
<span class=keyword>mod</span> ast <span class=punctuation>{</span>
    <span class=macro>nodes!</span><span class=punctuation>(</span><span class=comment>/* ... */</span><span class=punctuation>);</span>
<span class=punctuation>}</span></code></pre><p>This is because of the order in which Rust runs macros. <code>#[my_proc_macro]</code> runs first, because it could transform the output to be <em>literally anything</em> (see the <code>#[cfg]</code> attribute macros, which wholesale remove the thing they're applied to). What is this means, however, is that when <code>#[my_proc_macro]</code> runs, all it sees is a module with a single macro invocation, which it doesn't like, so it moves on without doing any work :(. Only then does <code>nodes!()</code> run and generate all the <code>struct</code>/<code>enum</code> definitions, too late.</p><p>Our goal is to have <code>#[my_proc_macro]</code> operate on a module with expanded <code>struct</code>/<code>enum</code> definitions. Knowing how Rust evaluates macros, we have two options:</p><ol><li>Have <code>#[my_proc_macro]</code> eagerly expand all macro invocations inside it.</li><li>Have <code>nodes!()</code> generate everything in one shot, applying <code>#[my_proc_macro]</code> to the finished product.</li></ol><p>(1) is not possible unless we're The Rust Compiler/willing to resort to dirty hacks, so we're left with (2).</p><h2><a aria-hidden=true class=anchor href=#generating-everything-in-one-pass id=heading-generating-everything-in-one-pass></a>Generating Everything In One Pass</h2><blockquote><p>Neat Trick 2: Inside a repetition <code>$()*</code>, you can use <code>$()?</code> like individual <code>macro_rules!</code> cases, and the results will be the same, so long as you use an appropriate delimiter<sup class=footnote-ref><a data-footnote-ref href=#fn-3 id=fnref-3>3</a></sup>.</p></blockquote><pre class=syntax-highlighting><code class=language-rust><span class=keyword>macro_rules</span>! nodes_prime <span class=punctuation>{</span>
    <span class=punctuation>(</span>
        $<span class=punctuation>(</span>
            $name<span class=punctuation>:</span>ident
            $<span class=punctuation>(</span>
                <span class=comment>// Case 1</span>
                <span class=punctuation>[</span> $a_subnode<span class=punctuation>:</span>ident <span class=punctuation>]</span>
            <span class=punctuation>)</span>?
            $<span class=punctuation>(</span>
                <span class=comment>// Case 2</span>
                <span class=punctuation>(</span> $<span class=punctuation>(</span>
                    <span class=operator>*</span> $b_field<span class=punctuation>:</span>ident <span class=punctuation>:</span> $b_ty<span class=punctuation>:</span>ty
                <span class=punctuation>)</span>+ <span class=punctuation>)</span>
            <span class=punctuation>)</span>?
            $<span class=punctuation>(</span>
                <span class=comment>// Case 3</span>
                <span class=punctuation>(</span> $<span class=punctuation>(</span>
                    + $c_ty<span class=punctuation>:</span>ident
                <span class=punctuation>)</span>+ <span class=punctuation>)</span>
            <span class=punctuation>)</span>?
            <span class=comment>// Delimiter</span>
            <span class=punctuation>;</span>
        <span class=punctuation>)</span><span class=operator>*</span>
    <span class=punctuation>)</span> => <span class=punctuation>{</span>
        <span class=attribute>#</span><span class=punctuation>[</span><span class=attribute>my_proc_macro</span><span class=punctuation>]</span>
        <span class=keyword>mod</span> ast <span class=punctuation>{</span>
            <span class=keyword>use</span> <span class=keyword>super</span><span class=punctuation>::</span><span class=operator>*</span><span class=punctuation>;</span>

        $<span class=punctuation>(</span>
            $<span class=punctuation>(</span>
                <span class=comment>// Case 1</span>
                <span class=keyword>pub</span> <span class=keyword>struct</span> $name<span class=punctuation>(</span><span class=constructor>Vec</span><span class=punctuation>&lt;</span>$a_subnode<span class=punctuation>>);</span>
            <span class=punctuation>)</span>?
            $<span class=punctuation>(</span>
                <span class=comment>// Case 2</span>
                <span class=keyword>pub</span> <span class=keyword>struct</span> $name <span class=punctuation>{</span> $<span class=punctuation>(</span>
                    $b_field<span class=punctuation>:</span> $b_ty<span class=punctuation>,</span>
                <span class=punctuation>)</span>+ <span class=punctuation>}</span>
            <span class=punctuation>)</span>?
            $<span class=punctuation>(</span>
                <span class=comment>// Case 3</span>
                <span class=keyword>pub</span> <span class=keyword>enum</span> $name <span class=punctuation>{</span> $<span class=punctuation>(</span>
                    $c_ty<span class=punctuation>(</span>$c_ty<span class=punctuation>),</span>
                <span class=punctuation>)</span>+ <span class=punctuation>}</span>
            <span class=punctuation>)</span>?
        <span class=punctuation>)</span><span class=operator>*</span>

        <span class=punctuation>}</span>
    <span class=punctuation>};</span>
<span class=punctuation>}</span></code></pre><p>Whoa! That's a big macro, huh? Hopefully each part makes sense given the previous two macros.</p><p>How this trick works is, whenever Rust encounters a series of <code>$()?</code>, it checks the next token to see which branch matches. Assuming exactly one does, it takes that branch and emits that case within the repetition. Neat! However, there are some downsides:</p><ol><li>The criteria for making <code>$()?</code> cases distinct is stricter than making <code>macro_rules!</code> cases distinct, due to the eager one-token lookahead.</li><li>You must make the variable names in each case distinct, which either leads to visual clutter (prefixes) or confusion (no prefixes).</li><li>The compiler will yell at you real good if you mess up at either of the above.</li></ol><p>Still, at least for me, the benefit of generating everything in one shot & being compatible with <code>#[my_proc_macro]</code> easily outweighs the cost. I hope to have a blog post soon about what exactly I'm <em>doing</em> in <code>#[my_proc_macro]</code> that makes it so cool (whoops I hinted at such a post <a href=https://wolfgirl.dev/blog/2024-11-24-a-novel-idea-about-functor-in-rust/>months ago already</a>, oh well). Anyways, take care, see you next time!!</p><section class=footnotes data-footnotes><ol><li id=fn-1><p>Apologies if you have trouble reading <code>macro_rules!</code> syntax from a cold start. I hope the usage example later helps. <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-1>↩</a></p></li><li id=fn-2><p>Yes, this list is complete, we're not forgetting angle <code>&lt;></code> braces, which need to be paired up in types. Rust doesn't force those to be paired up, because those tokens can also be part of comparison operators! <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-2>↩</a></p></li><li id=fn-3><p>We can even nest this trick as much as we want! That's how we get things like <a href=https://github.com/p0lyw0lf/pwcc/blob/bf75533bba40f9f0a06ab66a7807a5f87c72147e/pwcc/src/parser/macros.rs#L24-L174>this monstrosity</a> :3 <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-3>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[A Novel Idea About `Functor` In Rust?]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2024-11-24-a-novel-idea-about-functor-in-rust/" />
      <id>https://wolfgirl.dev/blog/2024-11-24-a-novel-idea-about-functor-in-rust/</id>
      <published>2024-11-24T22:24:16Z</published>
      <updated>2024-11-24T22:24:16Z</updated>
      <summary><![CDATA[I may have accidentally come up with a paper-worthy idea while just playing around with my silly compiler project]]></summary>
      <content type="html"><![CDATA[<p>So. In <a href=https://wolfgirl.dev/blog/2024-11-13-swift-almost-does-a-really-cool-thing-i-want/>my last post</a> about this compiler project, I wrote out the following trait definition, explaining that it's what I was using to do type-safe AST transformations:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Inner</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Output</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>;</span>
<span class=punctuation>}</span></code></pre><p>I called it a "specialized Functor" because I didn't quite understand what exactly a Functor was, but I knew that, at the very least, what I had was a little different.</p><p>Upon reading this, Alice of <a href=https://welltypedwit.ch/>welltypedwit.ch</a>, who actually knows what she's talking about when it comes to functional programming, <a href=https://bsky.app/profile/welltypedwit.ch/post/3latirgqtjk2h>pointed me towards</a> a Haskell package called <a href=https://hackage.haskell.org/package/uniplate-1.6.13>uniplate</a>, noting that what I was doing sounded like its <a href=https://hackage.haskell.org/package/uniplate-1.6.13/docs/Data-Generics-Uniplate-Operations.html#v:transformBi><code>transformBi</code></a> operation.</p><p>To fully understand what she meant by this, let's break down the type signature:</p><h2><a aria-hidden=true class=anchor href=#transformbi id=heading-transformbi></a><code>transformBi</code></h2><p>In Haskell:</p><pre class=syntax-highlighting><code class=language-haskell>transformBi :: Biplate from to => (to -> to) -> from -> from
</code></pre><p>What is this saying? Even if you understand Haskell, it can sometimes still be tricky to get what certain operations <em>mean</em>. In this case, I think about it like:</p><blockquote><p>Given a type <code>From</code>, which is a container type like the root of an AST, and a type <code>To</code>, which is like an inner node of an AST, transform <code>From</code> by applying an operation each time we encounter a node of type <code>To</code> in the tree.</p></blockquote><p>Indeed, that is very similar to what I was trying to do previously! So why did I call my trait <code>Functor</code>, when this description almost exactly matches? Well mostly out of ignorance, yes, but also, when I later looked up the equivalent Rust crate <a href=https://docs.rs/uniplate/latest/uniplate/trait.Biplate.html><code>uniplate</code></a>, its type signature looked something more like<sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>1</a></sup>:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>Biplate</span><span class=punctuation>&lt;</span><span class=type>To</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>transform_bi</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>op</span><span class=punctuation>:</span> <span class=keyword>fn</span><span class=punctuation>(</span><span class=type>To</span><span class=punctuation>)</span> -> <span class=type>To</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>;</span>
<span class=punctuation>}</span></code></pre><p>Quite different from what I ended up with! Note that this is <em>homogenous</em> in the types it maps over; it transforms ASTs to be ones of the same type, whereas I was interested in transforming ASTs to have <em>different types</em>, because the type of the AST can enforce specific invariants. So that's why I was, and still am, interested in something like <code>fmap</code> instead:</p><h2><a aria-hidden=true class=anchor href=#fmap id=heading-fmap></a><code>fmap</code></h2><p>In <a href=https://hackage.haskell.org/package/base-4.20.0.1/docs/Data-Functor.html>Haskell</a>:</p><pre class=syntax-highlighting><code class=language-haskell>fmap :: Functor f => (a -> b) -> f a -> f b
</code></pre><p>I think about this like:</p><blockquote><p>Given a container type <code>F</code> over homogenous elements <code>A</code>, you can transform each <code>A</code> into a <code>B</code> in order to get a new container <code>F</code> over homogenous elements <code>B</code>.</p></blockquote><p>And <em>that's</em> why I thought I wanted a <code>Functor</code>! Initially, I had only parameterized my AST on how it represented locations in memory. I had one type (<code>A</code>) for <code>HardwareRegister | StackAddress | PseudoRegister</code>, and another (<code>B</code>) for just <code>HardwareRegister | StackAddress</code>, in order to guarantee I could never emit assembly code that contained pseudo-registers. A regalloc<sup class=footnote-ref><a data-footnote-ref href=#fn-2 id=fnref-2>2</a></sup> pass would get rid of all the pseudo-registers, and I would run that pass with a specially-crafted <code>fmap</code> call.</p><p>I was inspired by how the Rust crate <a href=https://docs.rs/fmap/latest/fmap/trait.Functor.html><code>fmap</code></a> translated the Haskell signature<sup class=footnote-ref><a data-footnote-ref href=#fn-3 id=fnref-3>3</a></sup>:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Inner</span><span class=punctuation>;</span>  <span class=comment>// a.k.a. A</span>
    <span class=keyword>type</span> <span class=type>Mapped</span><span class=punctuation>;</span> <span class=comment>// a.k.a. F&lt;B></span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>op</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Inner</span><span class=punctuation>)</span> -> <span class=type>B</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>;</span>
<span class=punctuation>}</span></code></pre><p>Now you can hopefully understand how I ended up where I did! My version of <code>Functor</code> is exactly this, just with an explict associated parameter for <code>B</code> so writing generic implementations is easier<sup class=footnote-ref><a data-footnote-ref href=#fn-6 id=fnref-6>4</a></sup>.</p><h2><a aria-hidden=true class=anchor href=#going-beyond id=heading-going-beyond></a>Going beyond?</h2><p>While playing around with this <code>Functor</code> trait, I realized something interesting. Unlike Haskell's <code>Functor</code> typeclass, where <code>f</code> is necessarily a type with a generic parameter, and <code>fmap</code> necessarily transforms that generic parameter, Rust's <code>Functor</code> is a lot more like <code>Biplate</code>, in that you can specialize the transform to only be over certain inner elements!</p><p>When I realized this, I started trying to shoehorn everything into it. I didn't have to just write one <code>impl&lt;L: Location> Functor&lt;L> for Program&lt;L></code> implementation, I could write multiple<sup class=footnote-ref><a data-footnote-ref href=#fn-4 id=fnref-4>5</a></sup>!</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>Location</span><span class=punctuation>&lt;</span><span class=type>L</span><span class=punctuation>>(</span><span class=type>L</span><span class=punctuation>);</span> <span class=comment>// Needs to be a newtype so we can avoid potentially conflicting implementations</span>
<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>Input</span><span class=punctuation>,</span> <span class=type>Output</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Location</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>>></span> <span class=keyword>for</span> <span class=type>Program</span><span class=punctuation>&lt;</span><span class=type>Input</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>Location</span><span class=punctuation>&lt;</span><span class=type>Input</span><span class=punctuation>>;</span>
    <span class=keyword>type</span> <span class=type>Output</span> = <span class=type>Location</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>Program</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span> <span class=punctuation>{</span> ..<span class=punctuation>.</span><span class=property> </span><span class=punctuation>}</span><span class=property>
</span><span class=punctuation>}</span><span class=property>
</span><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>I</span><span class=punctuation>,</span><span class=property> </span><span class=type>O</span><span class=punctuation>></span><span class=property> </span><span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Expression</span><span class=punctuation>&lt;</span><span class=type>O</span><span class=punctuation>>></span><span class=property> </span><span class=keyword>for</span><span class=property> </span><span class=type>Program</span><span class=punctuation>&lt;</span><span class=type>I</span><span class=punctuation>></span><span class=property> </span><span class=punctuation>{</span><span class=property> ... </span><span class=punctuation>}</span><span class=property>
</span><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>I</span><span class=punctuation>,</span><span class=property> </span><span class=type>O</span><span class=punctuation>></span><span class=property> </span><span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Statement</span><span class=punctuation>&lt;</span><span class=type>O</span><span class=punctuation>>></span><span class=property> </span><span class=keyword>for</span><span class=property> </span><span class=type>Program</span><span class=punctuation>&lt;</span><span class=type>I</span><span class=punctuation>></span><span class=property> </span><span class=punctuation>{</span><span class=property> ... </span><span class=punctuation>}</span></code></pre><p>The restrictions of Rust's trait system, and needing to specify explicit <code>Input</code>/<code>Output</code>/<code>Mapped</code> types, actually makes <code>Functor</code> <em>more</em> flexible than its Haskell counterpart! I'm using it to create a class of functions that look like:</p><pre class=syntax-highlighting><code class=language-haskell>fmapBi :: Triplate from to => (to a -> to b) -> from a -> from b
</code></pre><p>and even <em>this</em>, despite being a pretty neat extension of both <code>Functor</code> and <code>Biplate</code> in Haskell, still doesn't capture the full generality of the Rust trait. Still, for my purposes, this "best of both worlds" is all I need: the ability to transform only specific inner nodes (from <code>Biplate</code>), and the ability to change the parameterization of the container type (from <code>Functor</code>).</p><p>I'd like to write a paper on this. The authors of <code>uniplate</code> <a href=https://ndmitchell.com/downloads/paper-uniform_boilerplate_and_list_processing-30_sep_2007.pdf>wrote one</a> explaining their approach, as did the author of <a href=https://hackage.haskell.org/package/multiplate><code>multiplate</code></a> (which I don't quite understand fully, TODO <a href=https://arxiv.org/pdf/1103.2841v1>read it</a> i guess). I'm not 100% sure I have a novel idea on my hands, but at the very least, I think it's a new useful way of looking at things that I'll be happy to include in my toy compiler.</p><hr><p>In case you're wondering, "PolyWolf, why has it taken you so long to get this post out? Surely you haven't spent over two weeks just thinking?", yes I have been thinking about this for a while, but mostly in the context of writing a proper Rust <code>proc_macro</code> that creates these implementations for me, just given the AST definition. It's unfortunately much much more involved than writing a <code>macro_rules!</code><sup class=footnote-ref><a data-footnote-ref href=#fn-5 id=fnref-5>6</a></sup>, especially trying to handle all the edge-cases properly. I don't know why I waited to publish this blog post until I finished writing this new macro; I didn't even need to explain anything about it in this post lol. Leaving that for a future blog post, maybe once I publish the crate perhaps !</p><p>As always, you can check out the code at <a href=https://github.com/p0lyw0lf/pwcc>github:p0lyw0lf/pwcc</a>; it <em>sort of</em> has documentation right now, if u squint</p><section class=footnotes data-footnotes><ol><li id=fn-1><p>The real thing's actually worse in my opinion: <code>fn transform_bi(&self, op: Arc&lt;dyn Fn(To) -> To>) -> Self</code>. You have to deal with the overhead of <code>Arc</code>, <code>dyn</code>, <em>and</em> <code>Clone</code>-ing your entire AST?? No thanks. This is actually one of the biggest motivators I have for wanting to write a paper about this, to show these operations can be done in-place, zero-copy (given a sufficiently smart compiler, I hope). <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-1>↩</a></p></li><li id=fn-2><p>(fake (deragatory)), I haven't gotten that far in the book yet, so all pseudo-registers just get assigned to unique stack addresses :) <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-2>↩</a></p></li><li id=fn-3><p>Again, this signature is a massive simplification. The real one cares about lifetimes, thread safety, etc. etc. all good stuff a proper Rust library needs to care about <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-3>↩</a></p></li><li id=fn-6><p>I'm reading this again and I'm actually not too convinced by that argument anymore. Maybe it was so I could write things like <code>TryFunctor</code> (returns a <code>Result</code>) or <code>AsyncFunctor</code> (returns an <code>impl Future</code>) without having to change the trait at all? That seems bad tho, and I should probably just write separate traits lol <a aria-label="Back to reference 4" class=footnote-backref data-footnote-backref data-footnote-backref-idx=4 href=#fnref-6>↩</a></p></li><li id=fn-4><p>Given appropriate macro/specialization support, which is what the previous post was about :P <a aria-label="Back to reference 5" class=footnote-backref data-footnote-backref data-footnote-backref-idx=5 href=#fnref-4>↩</a></p></li><li id=fn-5><p>I will curse dtolnay forever for making <code>syn</code>/<code>quote</code> the only way to do this, but maybe I'm just more frustated by how hard the logic is in general... <a aria-label="Back to reference 6" class=footnote-backref data-footnote-backref data-footnote-backref-idx=6 href=#fnref-5>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Swift *almost* does a really cool thing I want]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2024-11-13-swift-almost-does-a-really-cool-thing-i-want/" />
      <id>https://wolfgirl.dev/blog/2024-11-13-swift-almost-does-a-really-cool-thing-i-want/</id>
      <published>2024-11-13T13:36:20Z</published>
      <updated>2024-11-13T13:36:20Z</updated>
      <summary><![CDATA[On my [toy compiler](https://wolfgirl.dev/blog/2024-10-17-work-on-my-toy-compiler-is-progressing-smoothly/), I have a lot of AST manipula...]]></summary>
      <content type="html"><![CDATA[<p>On my <a href=https://wolfgirl.dev/blog/2024-10-17-work-on-my-toy-compiler-is-progressing-smoothly/>toy compiler</a>, I have a lot of AST manipulations I need to do. There is the "parsed" AST, the "IR" AST, and the "codegen" AST, all of which need various passes defined for them. Most passes boil down to "transform all nodes of a given type in-place". Now, because my ASTs are kinda big, and I have so many of them, and I have so many node types I want to map over, I <em>really</em> don't want to have to write out this mapping function by hand.</p><p>My solution for Rust was to write a "specialized <a href=https://hackage.haskell.org/package/base-4.20.0.1/docs/Data-Functor.html>Functor</a>" trait like so<sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>1</a></sup>:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Inner</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Output</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>;</span>
<span class=punctuation>}</span>

<span class=comment>// example of how it's implemented:</span>
<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>Inner</span><span class=punctuation>,</span> <span class=type>A</span><span class=punctuation>,</span> <span class=type>B</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Inner</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>Vec</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span>
<span class=keyword>where</span>
    <span class=type>T</span><span class=punctuation>:</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Inner</span><span class=punctuation>,</span> <span class=type>Input</span>=<span class=type>A</span><span class=punctuation>,</span> <span class=type>Output</span>=<span class=type>B</span><span class=punctuation>>,</span>
<span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>A</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Output</span> = <span class=type>B</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>Vec</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span> <span class=punctuation>{</span>
        <span class=variable>self</span><span class=punctuation>.</span><span class=function>into_iter</span><span class=punctuation>().</span><span class=function>map</span><span class=punctuation>(</span><span class=operator>&</span><span class=keyword>mut</span> |<span class=variable>x</span><span class=punctuation>:</span> <span class=type>T</span>| x<span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>)).</span><span class=function>collect</span><span class=punctuation>()</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>I call this a "specialized Functor" because, unlike the Haskell version, it allows for implementations on arbitrary container types (e.x. parent AST nodes), for specific inner types (e.x. child AST nodes). To give an extremely simplified example:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>Function</span> <span class=punctuation>{</span>
    <span class=property>name</span><span class=punctuation>:</span> <span class=type>String</span><span class=punctuation>,</span>
    <span class=property>body</span><span class=punctuation>:</span> <span class=type>Vec</span><span class=punctuation>&lt;</span><span class=type>Statement</span><span class=punctuation>>,</span>
<span class=punctuation>}</span>

<span class=keyword>enum</span> <span class=type>Statement</span> <span class=punctuation>{</span>
    <span class=constructor>Return</span> <span class=punctuation>{</span> <span class=property>val</span><span class=punctuation>:</span> <span class=type>isize</span> <span class=punctuation>},</span>
<span class=punctuation>}</span></code></pre><p>A hand-written <code>Functor</code> implementation for <code>isize</code> would look like</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>isize</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>isize</span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Output</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span> <span class=punctuation>{</span>
        <span class=function>f</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>)</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span>

<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>isize</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>Statement</span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Output</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>Statement</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span> <span class=punctuation>{</span>
        <span class=keyword>match</span> <span class=variable>self</span> <span class=punctuation>{</span>
            <span class=type>Statement</span><span class=punctuation>::</span><span class=constructor>Return</span> <span class=punctuation>{</span> val <span class=punctuation>}</span> => <span class=type>Statement</span><span class=punctuation>::</span><span class=type>Return</span> <span class=punctuation>{</span>
                <span class=property>val</span><span class=punctuation>:</span> val<span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>),</span>
            <span class=punctuation>},</span>
        <span class=punctuation>}</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span>

<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>isize</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>Program</span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Output</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>Program</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span> <span class=punctuation>{</span>
        <span class=type>Program</span> <span class=punctuation>{</span>
            <span class=property>name</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=property>name</span><span class=punctuation>,</span>
            <span class=property>body</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=property>body</span><span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>),</span>
        <span class=punctuation>}</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span>

<span class=attribute>#</span><span class=punctuation>[</span><span class=attribute>test</span><span class=punctuation>]</span>
<span class=keyword>fn</span> <span class=function>map_isize</span><span class=punctuation>()</span> <span class=punctuation>{</span>
    <span class=keyword>let</span> x = <span class=type>Program</span> <span class=punctuation>{</span>
        <span class=property>name</span><span class=punctuation>:</span> <span class=string>"main"</span><span class=punctuation>.</span><span class=function>into</span><span class=punctuation>(),</span>
        <span class=property>body</span><span class=punctuation>:</span> <span class=punctuation>[</span><span class=type>Statement</span><span class=punctuation>::</span><span class=type>Return</span> <span class=punctuation>{</span> <span class=property>val</span><span class=punctuation>:</span> <span class=constant>1</span> <span class=punctuation>}].</span><span class=function>into</span><span class=punctuation>(),</span>
    <span class=punctuation>};</span>
    <span class=comment>// Pretend we derived Clone</span>
    <span class=keyword>let</span> y = <span class=type>Functor</span><span class=punctuation>::&lt;</span><span class=type>isize</span><span class=punctuation>>::</span><span class=function>fmap</span><span class=punctuation>(</span>x<span class=punctuation>.</span><span class=function>clone</span><span class=punctuation>(),</span> |i| i + <span class=constant>1</span><span class=punctuation>);</span>
    <span class=comment>// Pretend we derived PartialEq</span>
    <span class=macro>assert_eq!</span><span class=punctuation>(</span>y<span class=punctuation>,</span> <span class=constructor>Program</span> <span class=punctuation>{</span>
        <span class=property>name</span><span class=punctuation>:</span> <span class=string>"main"</span><span class=punctuation>.</span><span class=function>into</span><span class=punctuation>(),</span>
        <span class=property>body</span><span class=punctuation>:</span> <span class=punctuation>[</span><span class=type>Statement</span><span class=punctuation>::</span><span class=constructor>Return</span> <span class=punctuation>{</span> <span class=property>val</span><span class=punctuation>:</span> <span class=constant>2</span> <span class=punctuation>}].</span><span class=function>into</span><span class=punctuation>(),</span>
    <span class=punctuation>});</span>
<span class=punctuation>}</span></code></pre><p>That is, we re-construct the value in-place, recursively calling <code>fmap</code> to transform the appropriate fields<sup class=footnote-ref><a data-footnote-ref href=#fn-3 id=fnref-3>2</a></sup>.</p><p>Now, because the entire point of this is for me to be really lazy, I did not want to have to write out all these <code>Functor</code> implementations manually. So naturally, I <a href=https://github.com/p0lyw0lf/pwcc/blob/6598df9aa48c28b4fe577540e7d824343510670f/functional/src/functor.rs>wrote a macro</a><sup class=footnote-ref><a data-footnote-ref href=#fn-2 id=fnref-2>3</a></sup> that does most of the work for me. All I have to do now is specify, for a given inner type, for each AST node above it in the tree, what fields I'd like to map over.</p><p>Unfortunately, that's still too much work!!! I have a lot of AST nodes and inner types I want to map over, so what I'd really like is a way to define something like this:</p><pre class=syntax-highlighting><code class=language-swift><span class=keyword>func</span> <span class=function>fmap</span><span class=operator>&lt;</span><span class=variable>Container</span><span class=punctuation>,</span> <span class=variable>Inner</span><span class=operator>></span><span class=punctuation>(</span><span class=variable>over</span> <span class=variable>c</span><span class=punctuation>:</span> <span class=type>Container</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=punctuation>(</span><span class=type>Inner</span><span class=punctuation>)</span> <span class=operator>-></span> <span class=type>Inner</span><span class=punctuation>)</span> <span class=operator>-></span> <span class=type>Container</span> <span class=punctuation>{</span>
  <span class=comment>// somehow `fmap` over all of `c`'s fields</span>
  <span class=keyword>return</span> <span class=variable>c</span>;
<span class=punctuation>}</span>

<span class=keyword>func</span> <span class=function>fmap</span><span class=operator>&lt;</span><span class=variable>Inner</span><span class=operator>></span><span class=punctuation>(</span><span class=variable>over</span> <span class=variable>v</span><span class=punctuation>:</span> <span class=type>Inner</span><span class=punctuation>,</span> <span class=variable>f</span> <span class=punctuation>(</span><span class=variable>Inner</span><span class=punctuation>)</span> <span class=operator>-></span> <span class=type>Inner</span><span class=punctuation>)</span> <span class=operator>-></span> <span class=type>Inner</span> <span class=punctuation>{</span>
  <span class=keyword>return</span> <span class=function>f</span><span class=punctuation>(</span><span class=variable>v</span><span class=punctuation>)</span>;
<span class=punctuation>}</span></code></pre><p>Basically: if we're at the target node type, just call the function (definition 2), otherwise, recursively call into all fields (definition 1). Easy as pie!</p><p>Wait a minute... that syntax highlighting... yep, this "pseudocode" is in fact, valid Swift! It even does exactly we want!!! See?</p><pre class=syntax-highlighting><code class=language-swift><span class=function>print</span><span class=punctuation>(</span><span class=function>fmap</span><span class=punctuation>(</span><span class=number>1</span><span class=punctuation>,</span> <span class=punctuation>{</span> <span class=variable>$0</span> <span class=operator>+</span> <span class=number>1</span> <span class=punctuation>}))</span>; <span class=comment>// 2</span>
<span class=function>print</span><span class=punctuation>(</span><span class=function>fmap</span><span class=punctuation>(</span><span class=string>"hello"</span><span class=punctuation>,</span> <span class=punctuation>{</span> <span class=variable>$0</span> <span class=operator>+</span> <span class=number>1</span> <span class=punctuation>}))</span>; <span class=comment>// "hello"</span>
<span class=function>print</span><span class=punctuation>(</span><span class=function>fmap</span><span class=punctuation>(</span><span class=string>"hello"</span><span class=punctuation>,</span> <span class=punctuation>{</span> <span class=variable>$0</span> <span class=operator>+</span> <span class=string>"_world"</span> <span class=punctuation>}))</span>; <span class=comment>// "hello_world"</span></code></pre><p>This kind of definition is impossible to do in Rust thanks to a little thing called <a href="https://doc.rust-lang.org/reference/items/implementations.html?highlight=coherence#trait-implementation-coherence">Coherence</a>, a.k.a. No <a href=https://rust-lang.github.io/rfcs/1210-impl-specialization.html>Specialization</a>. What Specialization allows us to do is write down those 2 definitions for <code>fmap</code>, and the compiler will automatically choose the "least generic" one according to some rules.</p><p>Great! Now all I have to do is implement some compile-time introspection to get it to map over the appropriate fields. How hard could that be?</p><p>[2 hours of trying to read Swift documentation and finding mostly AI-generated blog posts. Thank goodness for StackOverflow at least]</p><p>Upon looking further, it seems the built-in read-only reflection with <a href=https://developer.apple.com/documentation/swift/mirror>Mirror</a> only happens at runtime, and the <a href=https://github.com/wickwirew/Runtime>Runtime</a> read-write reflection library is same way (aptly-named). Also, there doesn't seem to be a way to dynamically call <code>fmap</code> based on the runtime type; instead, all recursive calls result in an <code>Any</code> type for <code>Container</code>, since that's all that can be resolved statically. By passing in types as arguments, I can get all the way down to "cannot convert value of type 'any Any.Type' to expected argument type '(any Any).Type'", which really illustrates the predicament.</p><p>I <em>might</em> be able to hack something with the <code>dynamic</code> keyword or like Protocols or whatever, but I'm still not pleased about runtime-only reflection. I am willing to pay the slight extra binary cost for all these definitions, and Swift doesn't let me do that. Unfortunate that this was a dead end, but still pretty fun to learn something new!</p><p>Think I'll try Haskell next? :P</p><section class=footnotes data-footnotes><ol><li id=fn-1><p>Note this trait has extra considerations for "what if the thing we're mapping over is polymorphic?". The complexity is about the same even without that consideration, unfortunately... <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-1>↩</a></p></li><li id=fn-3><p>Note the strange function invocation outside the trait. The compiler has a hard time inferring what <code>Functor</code> implementation to use otherwise, for some reason. That's how you know we're doing something cursed! <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-3>↩</a></p></li><li id=fn-2><p>(Not sure where to put this but it's still relevant for footnotes) I also had the idea of using this macro to define both <code>Functor&lt;Inner></code> and <code>Functor&lt;Result&lt;Inner, Err>></code> implementations. This works because <code>Inner</code> is never a <code>Result</code>, so everything has coherence! These latter implementations would have <code>Input=Inner, Output=Result&lt;Inner, Err></code> and could be configured to fail early or to collect all errors. Unfortunately, I ran into Coherence problems again when I tried to <code>impl Functor&lt;Result&lt;T, E>> for Vec&lt;Result&lt;T, E>></code> (for <code>Mapped=Result&lt;Vec&lt;T, E>></code>). I <em>could</em> just write another trait as well, but man that's annoying! <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-2>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[What If Anyone* Could Use My Post Composer?]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2024-11-08-what-if-anyone-could-use-my-post-composer-/" />
      <id>https://wolfgirl.dev/blog/2024-11-08-what-if-anyone-could-use-my-post-composer-/</id>
      <published>2024-11-08T18:22:28Z</published>
      <updated>2024-11-08T18:22:28Z</updated>
      <summary><![CDATA[A somewhat-common complaint I see on bsky/fedi is "gah, these darn character limits, if only i had somewhere to jot down longer posts..."]]></summary>
      <content type="html"><![CDATA[<p>A somewhat-common complaint I see on bsky/fedi is "gah, these darn character limits, if only i had somewhere to jot down longer posts, i would be incentivized to post to my blog most often. alas, i have to open a text editor on my computer and fumble around with <code>git</code>, which is simply too much work."</p><p>Which, like, fair! I actually had this exact same issue. And then, in the spirit of <a href=https://wolfgirl.dev/blog/2024-09-28-write-your-own-tools/>Write Your Own Tools</a>, I wrote my own post composer, as pictured here:</p><p><img alt src=https://static.wolfgirl.dev/polywolf/blog/01930c42-0ba7-7ff1-a6f4-8a540781bd7d/IMG_0208.jpeg></p><p>I've continued to add features since I first started using it too: drafts (with auto-save!), uploading attachments, and previews were all not part of the initial conception. But, because I Wrote My Own Tool, every time I felt it was lacking something, I could just add it.</p><p>Now that's great n all, but back to the original point: I started thinking, since I have this really neat tool that works well for my usecase, might other people find it suitable for their usecases too? How would I go about "productionizing" it? So here I am, brainstorming & writing down some proposed restrictions that would make it possible for me to do so in a fairly short timeframe:</p><ol><li>Users will log in with GitHub<sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>1</a></sup></li><li>Users will select a repository + branch + path they want to "post" to, and will have the ability to customize the filename and content according to templates (instructions/examples included)</li><li>Users will NOT have the ability to automatically upload attachments, like I do</li><li>Users will NOT have the ability to automatically make announcements on bksy/fedi, like I do</li></ol><p>These restrictions assume a workflow of "uses GitHub Actions to automatically update a website whenever a commit is added"<sup class=footnote-ref><a data-footnote-ref href=#fn-2 id=fnref-2>2</a></sup>, which I'm assuming is at least somewhat possible for the majority of people who would be interested in this tool (doesn't really help if you don't have automatic deploys set up already).</p><p>And now I reveal the true reason for this post: to gauge interest before I embark on changing my janky code to be slightly less janky for others to use. If you're reading this, and would be interested in using this tool, please leave a comment/contact me with something that includes the phrase "i'm interested in using your post composer". If the above restrictions preclude you from using this, please also let me know somehow. And as always, thanks for reading &lt;3</p><section class=footnotes data-footnotes><ol><li id=fn-1><p>This is the part I see the most people getting hung up on, b/c who's gonna trust a random person they've never met with "ability to act on your behalf on private repos" oauth permissions?? i sure wouldn't <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-1>↩</a></p></li><li id=fn-2><p>More security-conscious people may object to this as well, given GitHub's historically lax security regarding exposing repository secrets to workflows. They have been getting better tho! <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-2>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[More Automatic Crossposting!]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2024-10-18-more-automatic-crossposting-/" />
      <id>https://wolfgirl.dev/blog/2024-10-18-more-automatic-crossposting-/</id>
      <published>2024-10-19T03:16:22Z</published>
      <updated>2024-10-19T03:16:22Z</updated>
      <summary><![CDATA[I crossposted my [last blog post](https://wolfgirl.dev/blog/2024-10-17-work-on-my-toy-compiler-is-progressing-smoothly/) to my [Mastodon]...]]></summary>
      <content type="html"><![CDATA[<p>I crossposted my <a href=https://wolfgirl.dev/blog/2024-10-17-work-on-my-toy-compiler-is-progressing-smoothly/>last blog post</a> to my <a href=https://social.treehouse.systems/@PolyWolf>Mastodon</a> and <a href=https://bsky.app/profile/wolf.girl.technology>Bluesky</a> accounts, because I have no idea how much traffic my RSS feed is getting, and seeing funny numbers go up in response to people acknowledging my post feels good. I then realized, "hey wait a minute, I have <a href=https://github.com/p0lyw0lf/crossposter>this whole framework</a> for publishing to multiple platforms, which I'm using for this blog, and already have a Mastodon backend, so why don't I just hook that up too?" And then I hooked it up and all was well :)</p><p>...except, that isn't the entire story. I did not have a Bluesky backend before tonight, and adding one turned out to be a bit trickier than I intended.</p><h3><a aria-hidden=true class=anchor href=#the-easy-part id=heading-the-easy-part></a>The Easy Part</h3><p>Authentication was a lot simpler than Mastodon; Bluesky just uses password login. I'm fortunate there's a Python client for <a href=https://pypi.org/project/atproto/>atproto</a>, and my secrets system can handle sensitive data like passwords just fine, so hooking it in was no issue.</p><h3><a aria-hidden=true class=anchor href=#the-unexpectedly-hard-part id=heading-the-unexpectedly-hard-part></a>The Unexpectedly Hard Part</h3><p>Bluesky has pretty minimal rich text formatting: just links, hash-tags, & at-mentions. All I really wanted was links, and thought I could just drop in a URL into the post body & Bluesky clients would automatically make it clickable, just like Mastodon clients seem to do. Nope! Turns out, atproto has some sort of out-of-band (i.e. not contained in the text itself) data called "facets" for this purpose. Meaning I somehow had to construct a <a href=https://atproto.blue/en/latest/atproto_client/utils/text_builder.html#atproto_client.utils.text_builder.TextBuilder><code>TextBuilder</code></a> object with the link to my post formatted the way I desired.</p><p>If I were willing to hardcode things, this might have been easy. Unfortunately, I don't accept such half-measures. The rest of my backends work by running Jinja2 over a text file and then pushing that elsewhere, why shouldn't I be able to do the same with Bluesky? So I figured out a way. What my slowly-becoming-more-sleep-addled-by-the-second brain came up with was to use the <a href=https://github.com/miyuchina/mistletoe>mistletoe</a> markdown parsing library to traverse a markdown AST into a <code>TextBuilder</code>, like so:</p><pre class=syntax-highlighting><code class=language-python><span class=keyword>from</span> <span class=variable>atproto</span> <span class=keyword>import</span> <span class=variable>client_utils</span>
<span class=keyword>import</span> <span class=variable>mistletoe</span>
<span class=keyword>from</span> <span class=variable>mistletoe</span>.<span class=variable>base_renderer</span> <span class=keyword>import</span> <span class=constructor>BaseRenderer</span>

<span class=keyword>class</span> <span class=constructor>BlueskyRenderer</span>(<span class=constructor>BaseRenderer</span>):
    <span class=keyword>def</span> <span class=function>__init__</span>(<span class=variable>self</span>, <span class=operator>*</span><span class=variable>args</span>, <span class=operator>**</span><span class=variable>kwargs</span>):
        <span class=function>super</span>().<span class=property>__init__</span>(<span class=operator>*</span><span class=variable>args</span>, <span class=operator>**</span><span class=variable>kwargs</span>)
        <span class=variable>self</span>.<span class=property>builder</span> <span class=operator>=</span> <span class=variable>client_utils</span>.<span class=property>TextBuilder</span>()
        <span class=variable>self</span>.<span class=property>has_paragraph</span> <span class=operator>=</span> <span class=constant>False</span>

    <span class=keyword>def</span> <span class=function>render_inner</span>(<span class=variable>self</span>, <span class=variable>token</span>):
        <span class=keyword>for</span> <span class=variable>token</span> <span class=operator>in</span> <span class=variable>token</span>.<span class=property>children</span>:
            <span class=variable>self</span>.<span class=property>render</span>(<span class=variable>token</span>)
        <span class=keyword>return</span> <span class=variable>self</span>.<span class=property>builder</span>

    <span class=keyword>def</span> <span class=function>render_raw_text</span>(<span class=variable>self</span>, <span class=variable>token</span>):
        <span class=keyword>return</span> <span class=variable>self</span>.<span class=property>builder</span>.<span class=property>text</span>(<span class=variable>token</span>.<span class=property>content</span>)

    <span class=keyword>def</span> <span class=function>render_line_break</span>(<span class=variable>self</span>, <span class=variable>token</span>):
        <span class=keyword>return</span> <span class=variable>self</span>.<span class=property>builder</span>.<span class=property>text</span>(<span class=string>"\n"</span>)

    <span class=keyword>def</span> <span class=function>render_paragraph</span>(<span class=variable>self</span>, <span class=variable>token</span>):
        <span class=keyword>if</span> <span class=variable>self</span>.<span class=property>has_paragraph</span>:
            <span class=variable>self</span>.<span class=property>builder</span>.<span class=property>text</span>(<span class=string>"\n\n"</span>)
        <span class=variable>self</span>.<span class=property>has_paragraph</span> <span class=operator>=</span> <span class=constant>True</span>
        <span class=keyword>return</span> <span class=variable>self</span>.<span class=property>render_inner</span>(<span class=variable>token</span>)

    <span class=keyword>def</span> <span class=function>render_link</span>(<span class=variable>self</span>, <span class=variable>token</span>):
        <span class=keyword>return</span> <span class=variable>self</span>.<span class=property>builder</span>.<span class=property>link</span>(<span class=variable>token</span>.<span class=property>title</span>, <span class=variable>token</span>.<span class=property>target</span>)

    <span class=keyword>def</span> <span class=function>render_auto_link</span>(<span class=variable>self</span>, <span class=variable>token</span>):
        <span class=keyword>return</span> <span class=variable>self</span>.<span class=property>builder</span>.<span class=property>link</span>(<span class=variable>token</span>.<span class=property>target</span>, <span class=variable>token</span>.<span class=property>target</span>)

<span class=keyword>def</span> <span class=function>render_post</span>(<span class=variable>post</span>: <span class=type>str</span>) <span class=operator>-></span> <span class=variable>client_utils</span>.<span class=property>TextBuilder</span>:
    <span class=keyword>return</span> <span class=variable>mistletoe</span>.<span class=property>markdown</span>(<span class=variable>post</span>, <span class=constructor>BlueskyRenderer</span>)</code></pre><p>This is a bit janky, since the <code>render_*</code> functions are supposed to return a <code>str</code>, not a <code>TextBuilder</code>, but Python is dynamically typed so it all works out! Hopefully you've seen this for yourself, having been directed from one of the platforms I've crossposted this to :)</p><p><a href=https://wolfgirl.dev/blog/2024-09-28-write-your-own-tools/>Write Your Own Tools</a> philosophy strikes again!!</p>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[work on my toy compiler is progressing smoothly]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2024-10-17-work-on-my-toy-compiler-is-progressing-smoothly/" />
      <id>https://wolfgirl.dev/blog/2024-10-17-work-on-my-toy-compiler-is-progressing-smoothly/</id>
      <published>2024-10-18T02:23:46Z</published>
      <updated>2024-10-18T02:23:46Z</updated>
      <summary><![CDATA[as mentioned in my [projects](/projects/) page, i'm writing a toy compiler called [PolyWolf's C Compiler](https://github.com/p0lyw0lf/pwc...]]></summary>
      <content type="html"><![CDATA[<p>as mentioned in my <a href=/projects/>projects</a> page, i'm writing a toy compiler called <a href=https://github.com/p0lyw0lf/pwcc>PolyWolf's C Compiler</a> based off the book <a href=https://nostarch.com/writing-c-compiler>Writing a C Compiler</a>. I've used it as an excuse to do a good amount of Rust metaprogramming (<code>macro_rules!</code> only!!), and now that I've figured out a bunch of the tricky bits, it's been smooth sailing putting more recent chapters into my mostly-declarative framework.</p><p>my approach diverges a bit from the book, however, even by chapter 4 (out of many, many more) and I think that's important to write down. <code>wacc</code> does not distinguish between "all possible operands" and "all operands that can be written to", making it possible to represent illegal instructions like <code>mov $1, $2</code>. <code>pwcc</code> <em>does</em> make a distinction between these, respectively calling them "Val" & "Temporary" at the IR stage and "Operand" & "Location" at the assembly generation stage.</p><p>however, this doesn't resolve <em>all</em> issues. memory-to-memory <code>mov</code>s still aren't allowed & need to be dealt with the same way, by introducing an intermediate register<sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>1</a></sup>. it also has created more problems, like needing to allocate extra Temporaries in certain scenarios the result of an expression is a Val, when the original code just uses a register instead of stack space. my hope is that my structure is amenable to optimizations later, plus it just feels better to have more invalid states not representable via types :) I do wonder if I can make <em>all</em> invalid instructions not representable, i.e. by also distinguishing between register and memory operands earlier... maybe not worth it b/c better to consolidate that logic into the hardware pass at the end instead of forcing it to be in the IR -> Assembly pass? idk we'll see</p><section class=footnotes data-footnotes><ol><li id=fn-1><p>i don't feel great about this solution; the book allocates an entire <strong><em>2</em></strong> registers for this purpose, <code>%r10d</code> and <code>%r11d</code>, which i think is way too much for the limited number x86 has. <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-1>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[How should I store my blog posts?]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/7780963/" />
      <id>https://wolfgirl.dev/blog/7780963/</id>
      <published>2024-09-21T14:52:12Z</published>
      <updated>2024-09-21T14:52:12Z</updated>
      
      <content type="html"><![CDATA[<p>Originally published on Cohost.</p><hr><p>now that Astro has their <a href=https://astro.build/blog/astro-5-beta/#stable-content-layer>Content Layer API</a> coming in their next major release, I'm suddenly a lot more flexible in how I can host the markdown files that power my blog, no longer limited to just "checked in to the Git repo". To go along with a time-honored tradition, let's make a table!</p><table class=first-is-label><thead><tr><th></th><th>Git</th><th>S3</th><th>SQLite</th><th>Third-party CMS<th></tr></thead><tbody><tr><td>Backup-ability</td><td>Very, almost required</td><td>Manually, tied to provider's CLI</td><td>Manually, w/ scp</td><td>?</td></tr><tr><td>Snapshot-ability</td><td>Very, required</td><td>Somewhat, potentially automatic, but probably not going to</td><td>Somewhat, probably manual, but probably not going to</td><td>Probably Not<td></tr><tr><td>Astro Load-ability</td><td>Easy</td><td>Potentially easy</td><td>Potentially hard</td><td>Easy if supported, potentially hard if not<td></tr><tr><td>Use My Own Post Composer</td><td>Yes</td><td>Yes</td><td>Yes</td><td>No, but maybe that's better?<td></tr><tr><td>Editable w/o Post Composer</td><td>Yes</td><td>Not Easily</td><td>Even Less Easily</td><td>Likely No<td></tr><tr><td>Ease Of Post Composer Posting</td><td>Very</td><td>Pretty</td><td>Sort Of</td><td>Very<td></tr><tr><td>Ease Of Post Composer Editing</td><td>Not Very</td><td>Pretty</td><td>Sort Of</td><td>Very<td></tr><tr><td>Deployment Solution?</td><td>Already Built</td><td>Not Built</td><td>Not Built</td><td>Not Built<td></tr><tr><td>Supports Drafts?</td><td>No</td><td>Yes</td><td>Yes</td><td>Probably<td></tr><tr><td>Also Stores Media?</td><td>Yes, but it shouldn't</td><td>Yes</td><td>No</td><td>Probably?<td></tr><tr><td>Category</td><td>Git</td><td>S3</td><td>SQLite</td><td>Third-party CMS<td></tr></tbody></table><p>yep, as expected, making a table was very helpful! it really seems like Git is still the best solution, followed by S3 as a close second. Doing more research, it seems like "Git-based CMS" is a not-uncommon thing, so really I'm pretty good where I am already.</p><p>The biggest thing I might change is moving the image files out of my Git repository. It makes clones take a lot longer than needed, right now they're actually the largest part of redeploys. I <em>think</em> having them be hosted from a S3 + Cloudflare combination & using Astro's Remote Image Functionality will help speed up build times, because that <em>should</em> be faster than pulling them thru Git. idk tho we'll see.</p>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[OK Nevermind Lean 4 Is Kinda Bad Actually]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/7534408/" />
      <id>https://wolfgirl.dev/blog/7534408/</id>
      <published>2024-09-03T12:39:21Z</published>
      <updated>2024-09-03T12:39:21Z</updated>
      <summary><![CDATA[In which I express my disappointment that the new programming language I'm learning isn't so good after all...]]></summary>
      <content type="html"><![CDATA[<p>Originally posted on Cohost. I had planned to write <a href=/tags/pwcc>pwcc</a> in Lean 4, but after these discoveries & roadblocks, I gave up on it.</p><hr><h3><a aria-hidden=true class=anchor href=#the-binary-sizes id=heading-the-binary-sizes></a>The Binary Sizes</h3><p>My "hello world but it parses command line arguments" binary was a whopping 78 megabytes, and after adding a regex library (which I haven't yet ended up using in any codepaths that can be run) it's 83 megabytes now. Talking to my friend this is because "no optimizations are done" and "all symbols from all transitive dependencies are exported" and "mathlib (the largest one & sort of the reason for Lean's existence in the first place) is usually included somewhere in that chain"</p><h3><a aria-hidden=true class=anchor href=#the-error-messages id=heading-the-error-messages></a>The Error Messages</h3><p>Because of the whole "type universe" thing it has going on, you get some very strange errors:</p><ul><li>returning the wrong type of <code>Unit</code> (there is also a universe-polymorphic <code>PUnit</code>)</li><li>using the wrong level of <code>Option.none</code> inside an <code>Option</code> monad (when the return type of the outer function and the inner function belong to different type universes)</li></ul><p>Let alone all the error messages that come up when trying to do metaprogramming stuff..</p><p>A lot of this I think is because Lean is wayyy too flexible, so errors are reported far away from where the "wrong" thing actually happened. Writing <code>Unit -> T</code> when it really wanted <code>fun () => ({}: T)</code> is not necessarily easy to debug.</p><h3><a aria-hidden=true class=anchor href=#the-metaprogramming id=heading-the-metaprogramming></a>The Metaprogramming</h3><p>Lean 4 is supposed to have the most advanced metaprogramming in existence, and to some extent that's true! However, because it's so advanced, it only operates at the AST level & lacks the capabilities to insert all the ASTs it knows about. For example, I was trying to make a custom inductive datatype. In Rust, this looks something like <a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=370a6c198e6da266529a592c5916a5c1">this</a>, and translating that to Lean syntax, you'll get something like</p><pre class=syntax-highlighting><code class=language-lean4>macro t:ident "::=" (ids:ident),* : command => `(
  inductive $t where $[| $ids : $t]*
)
</code></pre><p>The problems with this you'll run into:</p><ol><li><code>macro</code> doesn't like arguments inside repeaters, you need to desugar to <code>syntax</code> + <code>macro_rules</code></li><li>the part that the <code>$[]*</code> replaces isn't a syntax category Lean recognizes</li></ol><p>Both of these generate a similar error message ("unexpected token") but (2) is the much more fundamental problem. If you try to desugar even more, you'll find that you're expected to make a syntax node of type <code>TSyntax `Lean.Parser.Command.ctor</code>. How do you get a syntax node of this type? Who knows!! If you fake it and try to case something that might look like it, you get bizarre error messages.</p><p>There's another thing you can do, which is to make an <code>elab</code> instead of a macro, but I also could not for the life of me figure out the magic incantation to make <code>Lean.Elab.Command.Declaration.inductDecl</code> stop giving me errors when I tried to <code>elabCommand</code> one with a single constructor.</p><p>In any case, I think I will go back to Rust metaprogramming, where we operate on plain-ol' token streams & the error messages make more sense to me</p>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Lean 4 Is A Cool Language]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/7506895/" />
      <id>https://wolfgirl.dev/blog/7506895/</id>
      <published>2024-09-01T03:05:22Z</published>
      <updated>2024-09-01T03:05:22Z</updated>
      <summary><![CDATA[In which I express my positive feelings about a new programming language I'm learning!]]></summary>
      <content type="html"><![CDATA[<p>Originally published on Cohost. Initially, I had planned on writing <a href=/tags/pwcc/>pwcc</a> in Lean 4, and this post was part of that exploratory process.</p><hr><p>Following along with <a href=https://lean-lang.org/lean4/doc/examples/bintree.lean.html>https://lean-lang.org/lean4/doc/examples/bintree.lean.html</a>, because i Just Want To Start Writing Code and reading examples is the best way to get acquainted.</p><p>Starting off, we have just a simple polymorphic tree definition:</p><pre class=syntax-highlighting><code class=language-lean><span class=keyword>inductive</span> <span class=variable>Tree</span> <span class=punctuation>(</span><span class=variable>β</span> <span class=punctuation>:</span> <span class=variable>Type</span> <span class=variable>v</span><span class=punctuation>)</span> <span class=keyword>where</span>
  <span class=operator>|</span> <span class=variable>leaf</span>
  <span class=operator>|</span> <span class=variable>node</span> <span class=punctuation>(</span><span class=variable>left</span> <span class=punctuation>:</span> <span class=variable>Tree</span> <span class=variable>β</span><span class=punctuation>)</span> <span class=punctuation>(</span><span class=variable>key</span> <span class=punctuation>:</span> <span class=variable>Nat</span><span class=punctuation>)</span> <span class=punctuation>(</span><span class=variable>value</span> <span class=punctuation>:</span> <span class=variable>β</span><span class=punctuation>)</span> <span class=punctuation>(</span><span class=variable>right</span> <span class=punctuation>:</span> <span class=variable>Tree</span> <span class=variable>β</span><span class=punctuation>)</span>
  <span class=keyword>deriving</span> <span class=variable>Repr</span></code></pre><p>And, skipping over a few things, both a naïve <code>toList</code> implementation and a tail-recursive one:</p><pre class=syntax-highlighting><code class=language-lean><span class=keyword>def</span> <span class=variable>Tree</span><span class=operator>.</span><span class=variable>toList</span> <span class=punctuation>(</span><span class=variable>t</span> <span class=punctuation>:</span> <span class=variable>Tree</span> <span class=variable>β</span><span class=punctuation>)</span> <span class=punctuation>:</span> <span class=variable>List</span> <span class=punctuation>(</span><span class=variable>Nat</span> × <span class=variable>β</span><span class=punctuation>)</span> <span class=operator>:=</span>
  <span class=keyword>match</span> <span class=variable>t</span> <span class=keyword>with</span>
  <span class=operator>|</span> <span class=variable>leaf</span> <span class=operator>=></span> <span class=punctuation>[]</span>
  <span class=operator>|</span> <span class=variable>node</span> <span class=variable>l</span> <span class=variable>k</span> <span class=variable>v</span> <span class=variable>r</span> <span class=operator>=></span> <span class=variable>l</span><span class=operator>.</span><span class=variable>toList</span> ++ <span class=punctuation>[(</span><span class=variable>k</span><span class=punctuation>,</span> <span class=variable>v</span><span class=punctuation>)]</span> ++ <span class=variable>r</span><span class=operator>.</span><span class=variable>toList</span>

<span class=keyword>def</span> <span class=variable>Tree</span><span class=operator>.</span><span class=variable>toListTR</span> <span class=punctuation>(</span><span class=variable>t</span> <span class=punctuation>:</span> <span class=variable>Tree</span> <span class=variable>β</span><span class=punctuation>)</span> <span class=punctuation>:</span> <span class=variable>List</span> <span class=punctuation>(</span><span class=variable>Nat</span> × <span class=variable>β</span><span class=punctuation>)</span> <span class=operator>:=</span>
  <span class=variable>go</span> <span class=variable>t</span> <span class=punctuation>[]</span>
<span class=variable>where</span>
  <span class=variable>go</span> <span class=punctuation>(</span><span class=variable>t</span> <span class=punctuation>:</span> <span class=variable>Tree</span> <span class=variable>β</span><span class=punctuation>)</span> <span class=punctuation>(</span><span class=variable>acc</span> <span class=punctuation>:</span> <span class=variable>List</span> <span class=punctuation>(</span><span class=variable>Nat</span> × <span class=variable>β</span><span class=punctuation>))</span> <span class=punctuation>:</span> <span class=variable>List</span> <span class=punctuation>(</span><span class=variable>Nat</span> × <span class=variable>β</span><span class=punctuation>)</span> <span class=operator>:=</span>
    <span class=keyword>match</span> <span class=variable>t</span> <span class=keyword>with</span>
    <span class=operator>|</span> <span class=variable>leaf</span> <span class=operator>=></span> <span class=variable>acc</span>
    <span class=operator>|</span> <span class=variable>node</span> <span class=variable>l</span> <span class=variable>k</span> <span class=variable>v</span> <span class=variable>r</span> <span class=operator>=></span> <span class=variable>go</span> <span class=variable>l</span> <span class=punctuation>((</span><span class=variable>k</span><span class=punctuation>,</span> <span class=variable>v</span><span class=punctuation>)</span> :: <span class=variable>go</span> <span class=variable>r</span> <span class=variable>acc</span><span class=punctuation>)</span></code></pre><p>Neat! Good to see that a functional programming language can in fact do functional programming things. Wait, what's this next paragraph:</p><blockquote>We now prove that <code>t.toList</code> and <code>t.toListTR</code> return the same list. The proof is on induction, and as we used the auxiliary function <code>go</code> to define <code>Tree.toListTR</code>, we use the auxiliary theorem <code>go</code> to prove the theorem.</blockquote><p>oh...</p><pre class=syntax-highlighting><code class=language-lean><span class=keyword>theorem</span> <span class=variable>Tree</span><span class=operator>.</span><span class=variable>toList_eq_toListTR</span> <span class=punctuation>(</span><span class=variable>t</span> <span class=punctuation>:</span> <span class=variable>Tree</span> <span class=variable>β</span><span class=punctuation>)</span>
        <span class=punctuation>:</span> <span class=variable>t</span><span class=operator>.</span><span class=variable>toList</span> <span class=operator>=</span> <span class=variable>t</span><span class=operator>.</span><span class=variable>toListTR</span> <span class=operator>:=</span> <span class=function>by
  simp </span><span class=punctuation>[</span><span class=variable>toListTR</span><span class=punctuation>,</span><span class=function> </span><span class=variable>go</span><span class=function> </span><span class=variable>t</span><span class=function> </span><span class=punctuation>[]]</span><span class=function>
</span><span class=variable>where</span><span class=function>
  </span><span class=variable>go</span><span class=function> </span><span class=punctuation>(</span><span class=variable>t</span><span class=function> </span><span class=punctuation>:</span><span class=function> </span><span class=variable>Tree</span><span class=function> </span><span class=variable>β</span><span class=punctuation>)</span><span class=function> </span><span class=punctuation>(</span><span class=variable>acc</span><span class=function> </span><span class=punctuation>:</span><span class=function> </span><span class=variable>List</span><span class=function> </span><span class=punctuation>(</span><span class=variable>Nat</span><span class=function> × </span><span class=variable>β</span><span class=punctuation>))</span><span class=function>
     </span><span class=punctuation>:</span><span class=function> </span><span class=variable>toListTR</span><span class=operator>.</span><span class=variable>go</span><span class=function> </span><span class=variable>t</span><span class=function> </span><span class=variable>acc</span> <span class=operator>=</span> <span class=variable>t</span><span class=operator>.</span><span class=variable>toList</span> ++ <span class=variable>acc</span> <span class=operator>:=</span> <span class=function>by
    </span><span class=variable>induction</span> <span class=variable>t</span> <span class=variable>generalizing</span> <span class=variable>acc</span> <span class=operator>&lt;</span><span class=punctuation>;</span><span class=operator>></span>
      <span class=variable>simp</span> <span class=punctuation>[</span><span class=variable>toListTR</span><span class=operator>.</span><span class=variable>go</span><span class=punctuation>,</span> <span class=variable>toList</span><span class=punctuation>,</span> <span class=operator>*</span><span class=punctuation>,</span> <span class=variable>List</span><span class=operator>.</span><span class=variable>append_assoc</span><span class=punctuation>]</span></code></pre><blockquote>The <code>[csimp]</code> annotation instructs the Lean code generator to replace any <code>Tree.toList</code> with <code>Tree.toListTR</code> when generating code.</blockquote><p>oh???</p><pre class=syntax-highlighting><code class=language-lean><span class=attribute>@[</span><span class=variable>csimp</span><span class=punctuation>]</span> <span class=keyword>theorem</span> <span class=variable>Tree</span><span class=operator>.</span><span class=variable>toList_eq_toListTR_csimp</span>
                 <span class=punctuation>:</span> @<span class=variable>Tree</span><span class=operator>.</span><span class=variable>toList</span> <span class=operator>=</span> @<span class=variable>Tree</span><span class=operator>.</span><span class=variable>toListTR</span> <span class=operator>:=</span> <span class=function>by
  </span><span class=variable>funext</span><span class=function> </span><span class=variable>β</span><span class=function> </span><span class=variable>t</span><span class=function>
  </span><span class=function>apply </span><span class=variable>toList_eq_toListTR</span></code></pre><p>Like i knew that this was the "functional programming used for proving things" language, but such a tight integration w/ the compiler itself is very cool. Formal verification ftw!</p>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Some Reasons Your React App Might Be Slow]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/5801824/" />
      <id>https://wolfgirl.dev/blog/5801824/</id>
      <published>2024-05-02T01:29:01Z</published>
      <updated>2024-05-02T01:29:01Z</updated>
      
      <content type="html"><![CDATA[<p>Originally published on Cohost in 2 parts.</p><hr><p><a href=https://mastodon.social/@danluu/112368308357914085>Dan Luu, on Mastodon</a>:</p><blockquote><p>Naive question: why do React apps in the real world tend to be slow?</p><p>I tried doing a React tutorial and the result was quite fast (w.r.t. latency & CPU utilization on low-end devices) until the tutorial has you replace "manual" / "low-level" react calls with commonly used libraries, e.g., using TanStack Query instead of useEffect plus a manually instantiated cache.</p><p>Is the main issue that libraries tend to be big and slow or is there another major cause of React app slowness?</p></blockquote><p>wow, a question i actually have a smidgen of expertise on!</p><p>At work, we have a very big React app. Here are some things which were causing slowness, which we know for certain because fixing them improved performance:</p><ol><li>Excessive use of react-redux and redux-saga. These are libraries which have you put everything you want to share between unrelated (not direct children) components in global state, and then runs "selectors" (arbitrary javascript functions to get specific data out of the global state) every time any of that state changes. Across thousands of selectors and many state changes per second, this really adds up. We have good reason to believe we're one of the largest users of Redux, so this might not be an issue for everyone.</li><li>Excessive rerenders, and non-memoized rerenders. You can have excessive rerenders when your component gets non-memoized objects (or, functions, arrays, etc.) as props. A child can do all the proper useCallback and useMemo it wants, but it's on the parent to not mess up. Which happens often since it's a pain to wrap every single arrow function in useCallback everywhere (React docs explicitly say to not do this until performance is a concern, even!). And even then, if a parent rerenders for whatever reason, it will rerender all its children, even if their props are all the same, unless the child is memoized, which is its own problem if the heirarchy is very deep.</li><li>Too Many DOM nodes. This is a general problem with component-like frameworks, encouraging a kind of abstraction leading to far too many wrapper divs since they're so easy to add. All these extra DOM nodes come at a surprising performance penalty, especially in browsers like Safari.</li></ol><p>Not quite an exhaustive "things that make your React app slow", but hopefully useful nonetheless :P</p><hr><h1><a aria-hidden=true class=anchor href=#other-reasons-your-very-big-website-might-be-very-slow id=heading-other-reasons-your-very-big-website-might-be-very-slow></a>Other Reasons Your Very Big Website Might Be Very Slow:</h1><ol start=4><li>Animations. The designer loves to go ham and make absolutely everything animated, but eventually yeah those start to add up too, especially when animating massive DOM trees and causing big layout shifts. Combined with (3) above, it sometimes takes a solid <strong>2 seconds</strong> to open a certain problematic side panel, on a good machine. Good luck on a little dinky machine. This problem is also exacerbated by using libraries like framer-motion, which have lots of cool animation curves, not all of them implemented efficiently! Using CSS animations where possible also lets you quickly turn them off with <code>animation: none !important; transition: none !important;</code></li><li>Opacity. The designer also loves using <code>rgba(255, 255, 255, 0.05)</code> for component backgrounds, because it layers nicely over almost any other background. Unfortunately, they require more work by the browser to render properly, which can amount to a surprising drop in performance on weaker clients. Especially when combined with animated transparencies🙀 Making an option to change to all flat colors was a noticeable gain. And this is all to say nothing of blur, which tanks performance even harder (we've almost got rid of it entirely now)</li><li>Excessive Repaints From Weird Layouts. Still haven't quite gotten to the bottom of this so can't say for sure, but it seems that certain layout things (CSS grid? Weird stacking contexts?) cause browsers to think they need to repaint more than what's actually changed, leading to a drop in performance. Saying "hey change the size of this one thing slightly" makes it go "oh i should repaint the whole screen, got it", even when there's no actual layout shift. This is once again exacerbated by animations, which cause this many times a second.</li></ol><p>Once again, these are just problems we've identified, and there may be more, I am by no means a Web Performance Expert. yet.</p>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[added this to my .zshrc]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/5425428/" />
      <id>https://wolfgirl.dev/blog/5425428/</id>
      <published>2024-04-04T22:35:35Z</published>
      <updated>2024-04-04T22:35:35Z</updated>
      <summary><![CDATA[In which I show off a pretty bizzare shell snippet & also explain it.]]></summary>
      <content type="html"><![CDATA[<p>Originally published on Cohost in 2 parts.</p><hr><pre class=syntax-highlighting><code class=language-zsh><span class=string>alias</span> <span class=string>prune</span>=<span class=string>"git remote prune origin | sed -n -E '/polywolf/ s/.*(polywolf.*)/\1/p' | xargs git branch -D"</span></code></pre><p>because i found myself typing it out <em>from memory</em> that often</p><p>oh no... I'm becoming one of Those girlies aren't i...</p><h2><a aria-hidden=true class=anchor href=#polywolf-what-does-this-do id=heading-polywolf-what-does-this-do></a>"PolyWolf, What Does This Do?"</h2><p>i'm glad you asked!! let's break it down:</p><blockquote><p><strong>NOTE</strong>: this assumes some familiarity with how git branches work. See <a href=https://wizardzines.com/comics/remote-branch-caching/>this</a> and potentially <a href=https://wizardzines.com/comics/branch-pointer-commit/>this</a> for quick explainer</p></blockquote><h2><a aria-hidden=true class=anchor href=#goals id=heading-goals></a>Goals</h2><p>in git, I like to use short-lived feature branches that I then rebase onto <code>main</code>, squash to a single commit, and delete<sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>1</a></sup>. Turns out this style of git is so common that GitHub has a dedicated option for doing this with a single click on a pull request.</p><p>The problem with the GitHub click is that, while the branch is deleted in the remote repository, the branch you were working on locally is still around and clutters up <code>git branch</code> output and any tab-complete things you have set up. So! What we want to do, then, is have a command that you can run periodically to delete local branches that were deleted on the remote.</p><h2><a aria-hidden=true class=anchor href=#getting-a-list-of-branches-to-delete id=heading-getting-a-list-of-branches-to-delete></a>Getting A List Of Branches To Delete</h2><p>This is actually a tricky UX question! How do you know the difference between a "deleted branch" and "a branch you haven't pushed yet"? The answer is, you don't! There is no way for git to know this information 🙂</p><p>But! We can still do something that's almost as good: getting a list of branches that have been deleted on the remote since the last time you did a prune. There is a command for this: <code>git remote prune origin</code>. This will look at all the locally-cached remote-tracking branches (the ones starting with <code>origin/</code>), and delete any that are no longer present on the remote.</p><p>This is <em>almost</em> exactly what we want. But as I said earlier, even if the branch <code>origin/polywolf/feature-branch</code> is deleted, <code>polywolf/feature-branch</code> will stick around. Fortunately, <code>git remote prune origin</code> prints out what it does. Unfortunately, there are no formatting options for its output, so we resort to Manually Parsing It.</p><h2><a aria-hidden=true class=anchor href=#editing-streams-with-the-stream-editor id=heading-editing-streams-with-the-stream-editor></a>Editing Streams With The Stream EDitor</h2><p>this is in fact what <code>sed</code> is meant to do! Let's break down the specific invocation I use:</p><h3><a aria-hidden=true class=anchor href=#the-arguments id=heading-the-arguments></a>The Arguments</h3><p>From <code>sed --help</code></p><ul><li><code>-n</code>: "suppress automatic printing of pattern space". Basically, only output lines when we tell it to</li><li><code>-E</code>: "use extended regular expressions in the script". Basically, allow for replacements by capture groups<sup class=footnote-ref><a data-footnote-ref href=#fn-3 id=fnref-3>2</a></sup>.</li><li>the last argument is interpreted as a sed script, which we will need to go to the manpage for</li></ul><h3><a aria-hidden=true class=anchor href=#the-script id=heading-the-script></a>The Script</h3><p>From <del><code>man sed</code></del> (let's be real here) searching on Stack Overflow:</p><ul><li><code>/polywolf/</code>: only run on lines that have "polywolf" in them</li><li><code>s/.*(polywolf.*)/\1/p</code>:<sup class=footnote-ref><a data-footnote-ref href=#fn-3 id=fnref-3-2>2</a></sup> For every line that looks like <code>* [pruned] origin/polywolf/feature-branch</code>, print out just <code>polywolf/feature-branch</code>. I verified this regex experimentally :)</li></ul><p>Great! We've collected all the branches we need into a "list" (newline-separated strings), and now we just need to delete them. But how?</p><h2><a aria-hidden=true class=anchor href=#deleting-a-branch id=heading-deleting-a-branch></a>Deleting A Branch</h2><p>The command to delete a branch is <code>git branch -d</code>. However, in this normal delete, git will complain the branch hasn't been merged. We don't care about this, because our feature branches have been effectively added as a commit on <code>main</code>, so we need to tell git "yes we really want to delete this branch", which we can do with <code>git branch -D</code>.</p><h3><a aria-hidden=true class=anchor href=#deleting-all-the-branches id=heading-deleting-all-the-branches></a>Deleting ALL The Branches</h3><p>The typical way of iterating over a list in bash-likes is</p><pre class=syntax-highlighting><code class=language-bash><span class=keyword>for</span> <span class=property>branch</span> <span class=keyword>in</span> <span class=embedded>$(</span><span class=function>the-expression</span><span class=embedded>)</span>
<span class=keyword>do</span>
  <span class=function>git</span> branch <span class=constant>-D</span> <span class=string>"</span><span class=operator>$</span><span class=property>branch</span><span class=string>"</span>
<span class=keyword>done</span></code></pre><p>This is the same as running <code>git branch -D b1; git branch -D b2; git branch -D b3; ...</code>, which, while somewhat inefficient from starting so many processes, Just Works™️. But! As it turns out, <code>git branch -D b1 b2 b3 ...</code> has the same effect, so we can use <code>xargs</code> (I absolutely adore <code>xargs</code>, it is so useful in one-liners).</p><p><code>xargs</code> takes in a "list" on standard input, and then turns that into an argument list it appends to whatever command you give it. So, given the list of branches we have currently, <code>xargs git branch -D</code> does exactly what we want!</p><p>And there you have it! A comprehensive explanation of a 1-liner I wrote for my own purposes that will probably be useful to no one else. This is the power of custom aliases/scripts, u can really tailor your own shell experience. it's almost <em>too</em> powerful... i still have a lot to learn tho!</p><section class=footnotes data-footnotes><ol><li id=fn-1><p>I did a couple internships at a <a href=https://www.mercurial-scm.org/>Mercurial</a> shop and have sworn by this model ever since. You Will Never<sup class=footnote-ref><a data-footnote-ref href=#fn-2 id=fnref-2>3</a></sup> Convince Me Merge Commits Are Good <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-1>↩</a></p></li><li id=fn-3><p>this is fancy Regex speak. don't worry about it kitten <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-3>↩</a> <a aria-label="Back to reference 2-2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2-2 href=#fnref-3-2>↩<sup class=footnote-ref>2</sup></a></p></li><li id=fn-2><p>ok Never is a strong word but u get my point <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-2>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[wow another weekend project]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/4999112/" />
      <id>https://wolfgirl.dev/blog/4999112/</id>
      <published>2024-03-10T14:15:22Z</published>
      <updated>2024-03-10T14:15:22Z</updated>
      <summary><![CDATA[I show off a (probably very bad) lock-free header-only C library.]]></summary>
      <content type="html"><![CDATA[<p>Originally published on Cohost.</p><hr><p>Was talking with a friend, and he said something to the effect of "man, if only I had a lock-free ringbuffer right now..." I thought I had one of those lying around somewhere, but turns out I only sort of did:</p><ul><li>It was written in Rust, not C (hard to include in a random C project)</li><li>It only supported pushes and pops of single elements (inefficient)</li><li>It suffered from multiple logic bugs (atomic programming is hard!)</li></ul><p>So! To prove to myself that I can still write C code in a pinch, I decided to knock one out real fast. And I did! <a href=https://github.com/p0lyw0lf/silly_ringbuffer>https://github.com/p0lyw0lf/silly_ringbuffer</a> took a bit longer than expected (setting up C environment on Windows, solving some hard bugs like ABA), but overall I am very satisfied with the results.</p><h3><a aria-hidden=true class=anchor href=#included-features id=heading-included-features></a>Included Features</h3><ul><li>Pushes and pops of arbitrary sizes (up to size of ringbuffer)</li><li>Threadsafe (safe to use without any external synchronization primitives)</li><li>Lock-free (only uses atomic variables internally)</li><li><del>Wait-free (threads always make progress if they can, and return an error if they can't)</del> maybe not?</li></ul><h3><a aria-hidden=true class=anchor href=#missing-features id=heading-missing-features></a>Missing Features</h3><ul><li>No support for custom allocators (just uses <code>malloc</code> and <code>free</code>, tho this is the easiest to remedy)</li><li>No growable buffer (size must be known up-front)</li><li>No "pop everything remaining" operation</li><li>No synchronization-primitive-enabled variant (would be more efficient in cases of multiple readers or writers, currently there is just one part where we have to spin which is bad)</li></ul><p>I might add these features sometime much later, but for now I consider it "good enough"; I have proven the point to myself, and my friend can continue with his own silly C project (he wanted a lock-free ringbuffer because "sharing a file across a <code>fork()</code>" isn't cross-platform enough, doesn't work on Windows).</p><p>Also, despite being a hackathon-like project, I always write my code with lots of documentation, so <a href=https://github.com/p0lyw0lf/silly_ringbuffer/blob/6400f311e0eb9f26f5bd5ed89c00a62b096a98dc/srb.h#L86>go read it!!!</a> you might learn something <del>or find another bug idk</del></p>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Weekend Project Complete]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/3587148/" />
      <id>https://wolfgirl.dev/blog/3587148/</id>
      <published>2023-11-19T15:42:55Z</published>
      <updated>2023-11-19T15:42:55Z</updated>
      
      <content type="html"><![CDATA[<p>Originally published on Cohost.</p><hr><p><a href=https://github.com/p0lyw0lf/crossposter>https://github.com/p0lyw0lf/crossposter</a></p><p>For a while, I've had a tradition of, whenever I would find a cool computer security-related article online, I would post it to a channel in a long-inactive Discord. I joined this Discord in my Freshman year of college, and I've been keeping up the tradition ever since, so I think I'm approaching 4 years of it now? Wild to think about.</p><p>The Problem with this is, my audience is limited. There are like <em>mayyyybe</em> 4 other people watching that channel, likely only 1. Plus, Discord is not a reliable store for information. So at one point, I decided to start backing things up <a href=https://wolfgirl.dev/cybersec/>on my blog</a>. Unfortunately, this was a very manual process, because interacting with Git is a lot harder than just pasting a link and some text into Discord, so the friction meant I very rarely updated that page.</p><p>During the initial Fediverse craze, one of my Solutions was to create an account, <a href=https://infosec.exchange/@PolyWolf>@PolyWolf@infosec.exchange</a>, that would take the place of my Discord channel. Problem was, that somehow ended up having even less of an audience because lol bootstrapping problem<sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>1</a></sup>. I was using that account more for following other tech accounts, than I was for posting myself.</p><p>So finally, while I was talking with the 1 friend who I knows follow the channel, I had an idea: why not write a little bot to automatically crosspost from my cozy Discord channel to these other platforms? That way, I could have the maximum audience with the least amount of effort.</p><p>The only thing stopping me was the good 'ol "I programmed too much at work this week so now I want to enjoy my weekend doing anything else but programming." Fortunately, this week was light, so I found the spoons Saturday, banging thru the full implementation in 8 hours<sup class=footnote-ref><a data-footnote-ref href=#fn-2 id=fnref-2>2</a></sup>, and spent a bit more time this morning setting up a t4g.micro instance on AWS to host it. And bam, it's done!</p><p>I have created a new Discord server for this bot, and will likely post my links there in the future. If this sounds interesting and u want the server invite, DM me @polywolf on Discord. Otherwise, once the new Cohost API rolls out, I'm probably also going to add a crossposting target for it under a new page.</p><p>In summary, I am excited I still have the ability to smash things together and have them work out. That's all for now, thanks for reading!</p><section class=footnotes data-footnotes><ol><li id=fn-1><p>I somehow overcame the bootstrapping problem for the second time in my life ever on Co Host Dot Org thanks to @fullmoon 's initial list from Twitter (now X) and also "being a techy trans furry on the techy trans furry website". My first time was on Scratch Dot MIT Dot EDU thanks to "being a kid making simple and fun games on the simple and fun games made by kids website". <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-1>↩</a></p></li><li id=fn-2><p>Why Python? For this exact reason. Unparalleled when it comes to gluing web things together. It was my first real programming language and I love it so. Like, i can just do <a href=https://github.com/p0lyw0lf/crossposter/blob/main/poster/template.py>stuff like this</a> and it just works because lol! This repo is definitely not a paragon of good Python style, but it sure is My Python style :) <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-2>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Software Packaging Is A Nightmare]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2613009/" />
      <id>https://wolfgirl.dev/blog/2613009/</id>
      <published>2023-09-24T23:23:42Z</published>
      <updated>2023-09-24T23:23:42Z</updated>
      
      <content type="html"><![CDATA[<p>Originally published on Cohost. I recall this one getting a fair amount of attention, which felt good because I put a fair amount of time into it.</p><hr><p>Recently, a friend shared <a href=https://gist.github.com/terabyte/15a2d3d407285b8b5a0a7964dd6283b0>this gist about how Amazon's internal buildsystem works</a>, and wow did that unlock some Opinions for me about software packaging.</p><p>Supposedly, Amazon's buildsystem "Brazil" works a little bit like <a href=https://nix.dev/tutorials/first-steps/>Nix</a>/<a href=https://nixos.org/manual/nixpkgs/stable/>NixPkgs</a><sup class=footnote-ref><a data-footnote-ref href=#fn-12 id=fnref-12>1</a></sup>, in that it has entirely reproducible builds based on package declarations of nearly every package in existence. But instead of just being able to choose a single snapshot of package versions<sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>2</a></sup>, you can also pin to <a href=https://semver.org/>semver</a> of a set of packages, whose mutual compatibility is automatically enforced via unit tests when creating a new immutable build, so you always have the latest updates. This is <em>amazing</em>. Also like Nix, you can have:</p><ul><li>Two versions of a package installed on your system, since different environments just choose which version they want</li><li>Local overrides of packages for development/debugging environments</li><li>Binary releases since everything is reproducible</li></ul><p>And it all "just works"! My friend who currently works there says he loves it and building software has never been easier. Which is wild, given that:</p><blockquote><p>The main build driver is a mess 'o Perl scripts that generates Makefiles. The build system is bootstrapped by a minimal Perl script which assumes only base Perl deps and GCC are available, and downloads all other dependencies.</p></blockquote><p>...but hey, if it works, it works!</p><h1><a aria-hidden=true class=anchor href=#most-software-isnt-like-this id=heading-most-software-isnt-like-this></a>Most Software Isn't Like This</h1><p>First, some terminology, so that it's unambiguous what I'm talking about:</p><ul><li><strong>Package</strong>: The atomic unit of software. Libraries, applications, etc. Each package has an: <ul><li><strong>Interface version</strong>: Some identifier for other software to know a package supports certain capabilities. Hopefully, specified in some <a href=https://semver.org/>semver</a>-compatible way, although in practice lol lmao. Adding extra debug logging or fixing security bugs that no legitimate consumer would be affected by won't change an interface version.</li><li><strong>Build version</strong>: Some identifier that can be 1-to-1 correlated with differences in the produced binary<sup class=footnote-ref><a data-footnote-ref href=#fn-8 id=fnref-8>3</a></sup> for a package. This lets you distinguish between "the library i built with extra logging and a patch for the vuln" and "the library without those things". Also accounts for differences in build versions of dependencies!</li></ul></li><li><strong>Dependency</strong>: a package that another package requires to build and/or run. Often specified with interface versions, but can also be specified against a build version.</li><li><strong>Version Set</strong>: A collection of build versions of packages known to work well together. It's important that <em>build versions</em> work well together and not <em>interface versions</em>, because it is very possible to mess up semver.</li><li><strong>Environment</strong>: For a specific package you want to use, the set of all other packages that are capable of affecting it.</li></ul><p>As far as I am aware, there are two common methods for distributing software packages and creating environments to run them in. These aren't the <em>only</em> methods in existence, and not everything can be categorized exactly, but they are archetypical.</p><h2><a aria-hidden=true class=anchor href=#share-everything id=heading-share-everything></a>Share Everything</h2><ul><li>There is one central version set, containing every package, often with testing that they all work together.</li><li>Only one build version per package can be installed at any given time. If you want to have different build versions at the same time, you need to create different packages or create package aliases.</li></ul><p>This is a <em>classic</em> model for software environments. <a href=https://archlinux.org/>Arch Linux</a>, <a href=https://www.redhat.com/en/technologies/linux-platforms/enterprise-linux>RHEL</a>, <a href=https://docs.python.org/3/installing/index.html>pip</a>, <a href=https://www.npmjs.com/>npm</a>, <a href=https://brew.sh/>Homebrew</a>, <a href=https://files.minecraftforge.net/net/minecraftforge/forge/>Forge</a>, if you can point at it and say "that's a Package Manager", it likely uses this model. There are differences in how often upgrades are released, how exactly semver pinning works, other jobs the package manager takes care of, but all of the examples I listed share these key traits<sup class=footnote-ref><a data-footnote-ref href=#fn-2 id=fnref-2>4</a></sup>.</p><p>Now, frankly, I think this model sucks big booty balls, pardon my language. Only being able to have <em>one</em> (1) version of a package officially installed is extremely limiting. Your package needs a build version of a dependency not in the central version set? Your options are to:</p><ol><li>Rename the dependency and install it globally.</li><li>"Install" the dependency outside the control of the package manager.</li><li>Give Up</li></ol><p>Option 1 is dumb because suddenly you are specifying interface/build versions as part of the package name, when it is literally the package manager's job to distinguish between these. Option 2 is dumb because suddenly you're rolling your own package manager with <code>CMakeLists.txt</code> and shell scripts when you have a perfectly good package manager right there<sup class=footnote-ref><a data-footnote-ref href=#fn-6 id=fnref-6>5</a></sup>. Option 3 is dumb because Winners Never Quit 😤</p><p>Sometimes, it's OK for packages to just live in their own little island of dependencies! Not everything needs to be global! It's frankly unacceptable that local installs are so poorly supported! However, it's possible to go too far the other way. Instead of sharing everything, you can instead:</p><h2><a aria-hidden=true class=anchor href=#share-nothing id=heading-share-nothing></a>Share Nothing</h2><p>If a package has dependencies, it will need to include its own way of putting them in its environment. There are ways for packages installed separately to be part of the same environment, but absent a package manager, these methods are rickety and best<sup class=footnote-ref><a data-footnote-ref href=#fn-3 id=fnref-3>6</a></sup> and infuriatingly buggy at worst. <em>Curiously enough</em>, consumer-grade operating systems like Windows and MacOS have this as their default approach. <em>Even more curiously</em>, a semi-recent push towards containerization technologies like <a href=https://www.docker.com/>Docker</a>, <a href=https://snapcraft.io/>snap</a>, <a href=https://www.flatpak.org/>flatpak</a>, and others have been pushing Linux software to be also distributed using this model. Why is this?</p><p>I hypothesize this model's popularity comes from the fact it is more likely to produce consistently working software. Linux distributions have been plagued by "works on my machine" or "works on my distro" problems for, like, ever. Share Everything, when you're trying to build packages outside the global version set, against different global version sets from different distros, or even the same distro as it evolves over time, is just <em>begging</em> to have frustrating build problems. <em>This</em> is why language-specific package managers with virtual environments feel like such an upgrade, <em>this</em> is why Docker is so popular. Your global environment has ghosts in it, invisible dependencies that haunt your build process, so isolating <em>everything</em> to trap the ghosts has its benefits for reproducibility.</p><p>Now, to be clear, the Share Nothing approach is not without its drawbacks. Requiring packages to bundle all their dependencies/exist within an isolated Share Everything fiefdom leads to a ton of bloat. I don't particularly <em>want</em> 5 copies of Tensorflow and PyTorch on my machine, but since I also don't want to try to shove all my one-off AI projects into a global Python environment, this is what I get stuck with.</p><h2><a aria-hidden=true class=anchor href=#what-the-approaches-are-missing id=heading-what-the-approaches-are-missing></a>What The Approaches Are Missing</h2><p>Let's lay out our <strong><em>Ideal Build System Requirements</em></strong> again:</p><ul><li>Reproducible builds: if a remote system can build it, your local system can too</li><li>Local overrides: not only can you build the package locally, you can swap it out for anything expecting that package</li><li>Remotely hosted binary releases: because you <em>deserve</em> to not have your fans take off and your disk explode every time you want to install some software</li><li>No global version set: you can have more than one version (major, minor, patch all included) of a package installed on your system, and things just don't care, since they use the one they were reproducibly built for</li><li>Semver <em>and</em> hash pinning: to enable dependency sharing if supported, but also for exact reproducibility if needed</li></ul><p>hm. Neither approach we've covered gets this right, or even comes close to doing all these<sup class=footnote-ref><a data-footnote-ref href=#fn-5 id=fnref-5>7</a></sup>! There are <strong>fundamental flaws in the way software packages are currently distributed</strong> that prevents these goals from being achievable.</p><h1><a aria-hidden=true class=anchor href=#boiling-the-ocean id=heading-boiling-the-ocean></a>Boiling The Ocean</h1><p>Now that we have a better understanding of what Share Everything and Share Nothing lack, we can appreciate why Amazon's Brazil is such an achievement. <em>Not only</em> does it let you isolate packages to only see their dependencies so everything is reproducible, it <em>also</em> lets packages share dependencies that provide the same interface version! Awesome! But what does it take to accomplish this, exactly?</p><h2><a aria-hidden=true class=anchor href=#technical-challenges id=heading-technical-challenges></a>Technical Challenges</h2><p>Not going to go too in-depth here (this post is long enough as it is LOL), but somewhat unsurprisingly, there aren't any compelling technical reasons we can't have this. Environment isolation at all levels has gotten quite good on the major OSes; why isn't that enough?</p><h2><a aria-hidden=true class=anchor href=#social-challenges id=heading-social-challenges></a>Social Challenges</h2><p>It's not that the technology doesn't exist, it's just that, <strong><em>no one has cared enough to put it into practice</em></strong>. "Why should <em>I</em> change how <em>I</em> build software", say the developers, the distro creators, "it works well enough for <em>my</em> use cases!".</p><p>Personally, I have built many pieces of software in environments just a teensy bit different from where they were supposed to be built, and have seen how bad it is. Every package is different, with its own magic incantations of scripts and command line flags and environment variables and build directories to get them working. As one commenter on the Brazil gist puts it:</p><blockquote><p>One major issue, in my experience, is the Brazil packaging concept doesn't have critical mass or would require boiling the ocean to get there. At Amazon, there's one package manager - Brazil. You want a Gem, NPM package, *.so, or JAR dependency? You add it to Brazil. Sometimes you go through great pain to add it to Brazil (especially when importing a new build system). But once the package is there, everything just works.</p></blockquote><p>Only nerds with obscene amounts of time on their hands have the ability to create an ecosystem. Package maintainers for Gentoo, NixPkgs, Guix, AUR, each take up their own divine hammers and bend the universe of software to their will. Everything "just works" when you're in the system, but for those of us unlucky enough to be developing software outside of it, we're living in an unending nightmare, where nothing works and everything is hard and no one has enough spare for a <a href=https://xkcd.com/927/>15th standard</a><sup class=footnote-ref><a data-footnote-ref href=#fn-13 id=fnref-13>8</a></sup> because we're just trying to build our silly little packages gosh darn it.</p><h2><a aria-hidden=true class=anchor href=#what-amazon-does id=heading-what-amazon-does></a>What Amazon Does</h2><p>In a nutshell, they throw money at the problem. Money for compute, to build <em>every single transitive dependent</em> when a package is published to make sure the listed interface version is semver-compliant. Money for storage, to host the entire history of software (source and binaries) so no deploys can possibly fail by missing old build versions. And most importantly, money for developers to port all software they want to use to this buildsystem.</p><p>This approach really only works for companies like Amazon, since the reward for them is in fact worth the cost. But what about the rest of us?</p><h2><a aria-hidden=true class=anchor href=#can-we-have-this-too id=heading-can-we-have-this-too></a>Can We Have This Too?</h2><p>I don't know, honestly. In my uninformed opinion, I'd probably say that NixPkgs and Guix are the closest things to what I've laid out, hitting all my Ideal Build System Requirements minus ones that are impossible thanks to No Money (semver pinning, total history<sup class=footnote-ref><a data-footnote-ref href=#fn-10 id=fnref-10>9</a></sup>). I have not used either extensively enough to judge the UX fully<sup class=footnote-ref><a data-footnote-ref href=#fn-9 id=fnref-9>10</a></sup>, though I will say I have heard terrible things about NixPkgs<sup class=footnote-ref><a data-footnote-ref href=#fn-11 id=fnref-11>11</a></sup> and nothing about Guix, neither of which is a good sign.</p><p>As an individual, it is not worth it for me to boil the ocean. I've gotten used to living in the nightmare, holding my Windows development environment together with duct tape & superglue, so I don't think I'll be making the switch any time soon. However, it takes a community to boil the ocean, so maybe now that my Arch install has collapsed in on itself, my next Linux install will be a reproducible one. I can only hope others feel the same.</p><section class=footnotes data-footnotes><ol><li id=fn-12><p>For a great explanation of the differences between these, see <a href=https://www.haskellforall.com/2022/08/stop-calling-everything-nix.html>this excellent blog post</a> by @fullmoon <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-12>↩</a></p></li><li id=fn-1><p>I don't actually know Nix that well, this was just my impression based on limited use + doc reading, please correct me on this point if I'm wrong! <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-1>↩</a></p></li><li id=fn-8><p>"What about source-only languages??" the source is a form of binary :) <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-8>↩</a></p></li><li id=fn-2><p>I think? Reasonable people could disagree on some aspects, maybe i am generalizing too hard but u tell me <a aria-label="Back to reference 4" class=footnote-backref data-footnote-backref data-footnote-backref-idx=4 href=#fnref-2>↩</a></p></li><li id=fn-6><p>Now to be fair, a lot of language-specific package managers <em>do</em> support having "local installs" of packages that read from a specific directory instead of from a repository. This is more a dig at the worst languages for packaging, C and C++, but still applies for how you get other languages' custom packages into local directories in the first place. <a aria-label="Back to reference 5" class=footnote-backref data-footnote-backref data-footnote-backref-idx=5 href=#fnref-6>↩</a></p></li><li id=fn-3><p>On Windows, you can put your .dll or .exe into the global <code>PATH</code> for other packages to access it<sup class=footnote-ref><a data-footnote-ref href=#fn-7 id=fnref-7>12</a></sup>, but this has the drawback of "oh shoot I'm using the wrong version of python.exe again, better reorder my <code>PATH</code>", so usually it's just best practice to put all the .dlls a package will need in the same folder. if you forget this best practice, your app runs with elevated privileges, and the <code>PATH</code> can somehow contain a user-writable folder, <a href=https://stackoverflow.com/questions/3623490/what-is-dll-hijacking>oops! DLL hijacking</a>. I think MacOS works similarly, but with better sandboxing in certain areas (so package files are only accessible by the package itself really, i think it is still susceptible forms of dll hijacking). for docker, u share dependencies through exposing network ports or shared folders, which, lol lmao. <a aria-label="Back to reference 6" class=footnote-backref data-footnote-backref data-footnote-backref-idx=6 href=#fnref-3>↩</a></p></li><li id=fn-5><p>Honestly? I believe that Cargo, the build system for Rust packages, comes the closest! Unfortunately the constraints of the language make binary releases nearly impossible (generics), and semver a thing only for source distributions. Plus, unless you're <a href=https://github.com/tosc-rs/mnemos>a madwoman</a>, Cargo isn't really suited for non-Rust packages. <a aria-label="Back to reference 7" class=footnote-backref data-footnote-backref data-footnote-backref-idx=7 href=#fnref-5>↩</a></p></li><li id=fn-13><p>It's also a hurdle that proprietary software won't adopt a model like this unless it is <em>the</em> standard, so anything that tries will have to bend over backwards for proprietary software, even more than for weird build scripts. But I digress, since this is ALREADY TOO LONG <a aria-label="Back to reference 8" class=footnote-backref data-footnote-backref data-footnote-backref-idx=8 href=#fnref-13>↩</a></p></li><li id=fn-10><p>https://hachyderm.io/@fasterthanlime/111103916027416609 <a aria-label="Back to reference 9" class=footnote-backref data-footnote-backref data-footnote-backref-idx=9 href=#fnref-10>↩</a></p></li><li id=fn-9><p>And when I did give it a little whirl, it promptly ate up all 72GB of my free disk space, which is maybe on me, but also, that's a lot of disk space!! so i haven't used it since. <a aria-label="Back to reference 10" class=footnote-backref data-footnote-backref data-footnote-backref-idx=10 href=#fnref-9>↩</a></p></li><li id=fn-11><p>https://blog.wesleyac.com/posts/the-curse-of-nixos , among others <a aria-label="Back to reference 11" class=footnote-backref data-footnote-backref data-footnote-backref-idx=11 href=#fnref-11>↩</a></p></li><li id=fn-7><p>Another thing you can do is register a different global environment variable just for your package, and then anyone looking for your package can simply read from that environment variable. For Docker, <code>s/environment variable/network port/g</code>. Like I said, rickety at best, infuriatingly buggy at worst :P <a aria-label="Back to reference 12" class=footnote-backref data-footnote-backref data-footnote-backref-idx=12 href=#fnref-7>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Learning Memory Order in C With Mutexes]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2091838/" />
      <id>https://wolfgirl.dev/blog/2091838/</id>
      <published>2023-08-10T01:13:59Z</published>
      <updated>2023-08-10T01:13:59Z</updated>
      <summary><![CDATA[In which I (hopefully simply) explain C11's memory orderings using mutexes as a motivating example.]]></summary>
      <content type="html"><![CDATA[<p>Originally published on Cohost.</p><hr><p>oh wow i have been mega procrastinating on this. <a href=/blog/2064554/>read this first btw</a></p><h2><a aria-hidden=true class=anchor href=#lets-talk-about-volatile id=heading-lets-talk-about-volatile></a>Let's Talk About <code>volatile</code></h2><p>So. <code>volatile</code>. The embedded programmer's best friend. But what does it actually mean?</p><p>From <a href=https://en.cppreference.com/w/c/language/volatile>cppreference</a>, emphasis mine:</p><blockquote><p>Every access (both read and write) made through an lvalue expression of volatile-qualified type is considered an observable side effect for the purpose of optimization and is evaluated strictly according to the rules of the abstract machine (that is, all writes are completed at some time before the next sequence point). This means that within <strong>a single thread of execution</strong>, a volatile access cannot be optimized out or reordered <strong>relative to another visible side effect</strong> that is separated by a sequence point from the volatile access.</p></blockquote><p>More simply, volatile reads and writes need to happen in the same order as you wrote them down in, but only with respect to "observable side effects" like syscalls or other volatile accesses.</p><p>So if you tried to use a standard <code>volatile int</code> for a mutex (ignoring the race condition and only focusing on a single thread):</p><pre class=syntax-highlighting><code class=language-c><span class=type>void</span> <span class=function>lock</span>(<span class=keyword>volatile</span> <span class=type>int</span><span class=operator>*</span> <span class=variable>m</span>) {
  <span class=type>int</span> <span class=variable>old_m</span> <span class=operator>=</span> <span class=number>1</span>;
  <span class=keyword>while</span> (<span class=variable>old_m</span>) {
    <span class=variable>old_m</span> <span class=operator>=</span> <span class=operator>*</span><span class=variable>m</span>;
    <span class=operator>*</span><span class=variable>m</span> <span class=operator>=</span> <span class=number>1</span>;
  }
}

<span class=type>void</span> <span class=function>unlock</span>(<span class=keyword>volatile</span> <span class=type>int</span><span class=operator>*</span> <span class=variable>m</span>) {
  <span class=operator>*</span><span class=variable>m</span> <span class=operator>=</span> <span class=number>0</span>;
}

<span class=type>void</span> <span class=function>do_stuff</span>(<span class=keyword>volatile</span> <span class=type>int</span><span class=operator>*</span> <span class=variable>m</span>, <span class=type>int</span><span class=operator>*</span> <span class=variable>shared</span>) {
  <span class=function>lock</span>(<span class=variable>m</span>);
  <span class=operator>*</span><span class=variable>shared</span> <span class=operator>=</span> <span class=number>5</span>;
  <span class=function>unlock</span>(<span class=variable>m</span>);
}</code></pre><p>Then, because the store to <code>shared</code> is non-volatile, it is not considered an observable side-effect, and can be freely reordered outside the critical section. You may then think "oh I'll just make everything inside the critical region volatile too!" but (1) this severely limits the optimizations compilers can do (those babies <em>love</em> to reorder stuff) and (2) that's not even the whole problem here. Remember, <code>volatile</code> only gives guarantees about single-threaded programs, and says nothing about multi-threaded ones.</p><details><summary>Note about the race condition:</summary> The swap of <code>old_m</code> and <code>*m</code> in the while loop isn't atomic, so an appropriate interleaving of two threads calling <code>lock()</code> could both enter the critical section, breaking mutual exclusion.</details><h2><a aria-hidden=true class=anchor href=#data-races id=heading-data-races></a>Data Races</h2><p>What is a data race? Since we're using C, we can check <a href=https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2310.pdf>the C standard</a>, section 5.1.2.4. Paragraph 4 says (emphasis theirs):</p><blockquote><p>Two expression evaluations <em>conflict</em> if one of them modifies a memory location and the other one reads or modifies the same memory location.</p></blockquote><p>and Paragraph 35 says:</p><blockquote><p>The execution of a program contains a <em>data race</em> if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.</p></blockquote><p>(where "before" is precisely defined in the other paragraphs).</p><p>With this in mind, let's take a look at my first attempt at the ticket lock implementation:</p><pre class=syntax-highlighting><code class=language-c><span class=keyword>#include</span> <span class=string>&lt;stdatomic.h></span>

<span class=keyword>typedef</span> <span class=keyword>struct</span> {
  <span class=type>atomic_int</span> <span class=property>next_ticket</span>;
  <span class=type>int</span> <span class=property>now_serving</span>;
} <span class=type>mutex_t</span>;

<span class=type>void</span> <span class=function>lock</span>(<span class=type>mutex_t</span> <span class=operator>*</span><span class=variable>m</span>) {
  <span class=type>int</span> <span class=variable>ticket</span> <span class=operator>=</span> <span class=function>atomic_fetch_add_explicit</span>(<span class=operator>&</span><span class=variable>m</span><span class=operator>-></span><span class=property>next_ticket</span>, <span class=number>1</span>, <span class=variable>memory_order_relaxed</span>);
  <span class=keyword>while</span> (<span class=variable>ticket</span> <span class=operator>!=</span> <span class=variable>m</span><span class=operator>-></span><span class=property>now_serving</span>) {}
}

<span class=type>void</span> <span class=function>unlock</span>(<span class=type>mutex_t</span> <span class=operator>*</span><span class=variable>m</span>) {
  <span class=operator>++</span><span class=variable>m</span><span class=operator>-></span><span class=property>now_serving</span>;
}</code></pre><p>Assume all uses of this API are correct (i.e. <code>lock()</code> always called before <code>unlock()</code>, no double-locking, and no double-unlocking within a thread). Can you spot the data race?</p><details><summary>(click for answer)</summary> That's right! <code>m->now_serving</code> is written to in <code>unlock()</code> and read from in <code>lock()</code> at the same time, and neither operation is atomic. If we assume that the compiler is not that smart and diligently translates our code into the assembly in the way we'd expect, then this works out fine: write before read means you see the write immediately, read before write is fine since we'll get it on the next iteration. But the compiler reasons that in the while loop, the value either <em>can</em> change (data race UB, since the loop is empty and there are no atomic updates in our program), or the value never changes (infinite loop UB. yes, certain non-terminating programs are UB) and can shoot our foot with whatever gun it likes.</details><p>Not to be deterred, I quickly fixed it:</p><pre class=syntax-highlighting><code class=language-c><span class=keyword>#include</span> <span class=string>&lt;stdatomic.h></span>

<span class=keyword>typedef</span> <span class=keyword>struct</span> {
  <span class=type>atomic_int</span> <span class=property>next_ticket</span>;
  <span class=type>atomic_int</span> <span class=property>now_serving</span>;
} <span class=type>mutex_t</span>;

<span class=type>void</span> <span class=function>lock</span>(<span class=type>mutex_t</span> <span class=operator>*</span><span class=variable>m</span>) {
  <span class=type>int</span> <span class=variable>ticket</span> <span class=operator>=</span> <span class=function>atomic_fetch_add_explicit</span>(<span class=operator>&</span><span class=variable>m</span><span class=operator>-></span><span class=property>next_ticket</span>, <span class=number>1</span>, <span class=variable>memory_order_relaxed</span>);
  <span class=keyword>while</span> (<span class=variable>ticket</span> <span class=operator>!=</span> <span class=variable>m</span><span class=operator>-></span><span class=property>now_serving</span>) {}
}

<span class=type>void</span> <span class=function>unlock</span>(<span class=type>mutex_t</span> <span class=operator>*</span><span class=variable>m</span>) {
  <span class=function>atomic_fetch_add_explicit</span>(<span class=operator>&</span><span class=variable>m</span><span class=operator>-></span><span class=property>now_serving</span>, <span class=number>1</span>, <span class=variable>memory_order_relaxed</span>);
}</code></pre><p>Great! Now that one of the updates is atomic, we've fixed the UB! That means we're done, right?</p><h2><a aria-hidden=true class=anchor href=#memory-ordering id=heading-memory-ordering></a>Memory Ordering</h2><p>Not so fast! So far, we've just been setting the memory ordering parameter to the atomic operations to <code>memory_order_relaxed</code>, since that's the most flexible for the compiler<sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>1</a></sup> and we only need atomic updates on individual variables to prevent data races.</p><p>However! Just like with <code>volatile</code>, C compilers are very aggressive about inlining functions and reordering load and stores, and since <code>memory_order_relaxed</code> doesn't impose any constraints on how the non-atomic loads and stores are sequenced, the compiler is still free to do whatever it wants. We're building a mutex after all, so it doesn't count if all we do is ensure the <code>lock()</code> and <code>unlock()</code> functions are race-free; we also need to make sure operations done in the critical section can't get reordered outside the <code>lock()</code> and <code>unlock()</code> calls.</p><p>Looking at the appropriate <a href=https://en.cppreference.com/w/c/atomic/memory_order>cppreference page</a> for other <code>memory_order</code> options, we see a couple that look interesting:</p><blockquote><p><code>memory_order_acquire</code>: A load operation with this memory order performs the acquire operation on the affected memory location: no reads or writes in the current thread can be reordered before this load. All writes in other threads that release the same atomic variable are visible in the current thread (see <a href=https://en.cppreference.com/w/c/atomic/memory_order#Release-Acquire_ordering>Release-Acquire</a> ordering)</p></blockquote><blockquote><p><code>memory_order_release</code>: A store operation with this memory order performs the release operation: no reads or writes in the current thread can be reordered after this store. All writes in the current thread are visible in other threads that acquire the same atomic variable (see <a href=https://en.cppreference.com/w/c/atomic/memory_order#Release-Acquire_ordering>Release-Acquire</a> ordering)</p></blockquote><p>Hey wait a minute, those names are familiar! "Acquire" is a fancy name for <code>lock()</code> and "release" is a fancy name for <code>unlock()</code>. These are memory orderings <em>specifically made for mutexes</em>; "reads or writes in the current threads" applies to <em>all</em> reads or writes, not just <code>volatile</code> ones. <code>memory_order_acquire</code> makes it so nothing in the critical section can be ordered before a <code>lock()</code>, and <code>memory_order_release</code> makes it so nothing in the critical section can be ordered after an <code>unlock()</code>. Perfect! With this in mind, we can write our final, correct<sup class=footnote-ref><a data-footnote-ref href=#fn-2 id=fnref-2>2</a></sup> ticket lock implementation:</p><pre class=syntax-highlighting><code class=language-c><span class=keyword>#include</span> <span class=string>&lt;stdatomic.h></span>

<span class=keyword>typedef</span> <span class=keyword>struct</span> {
  <span class=type>atomic_int</span> <span class=property>next_ticket</span>;
  <span class=type>atomic_int</span> <span class=property>now_serving</span>;
} <span class=type>mutex_t</span>;

<span class=type>void</span> <span class=function>lock</span>(<span class=type>mutex_t</span> <span class=operator>*</span><span class=variable>m</span>) {
  <span class=type>int</span> <span class=variable>ticket</span> <span class=operator>=</span> <span class=function>atomic_fetch_add_explicit</span>(<span class=operator>&</span><span class=variable>m</span><span class=operator>-></span><span class=property>next_ticket</span>, <span class=number>1</span>, <span class=variable>memory_order_relaxed</span>);
  <span class=keyword>while</span> (<span class=variable>ticket</span> <span class=operator>!=</span> <span class=variable>m</span><span class=operator>-></span><span class=property>now_serving</span>) {}
  <span class=function>atomic_thread_fence</span>(<span class=variable>memory_order_acquire</span>);
}

<span class=type>void</span> <span class=function>unlock</span>(<span class=type>mutex_t</span> <span class=operator>*</span><span class=variable>m</span>) {
  <span class=function>atomic_fetch_add_explicit</span>(<span class=operator>&</span><span class=variable>m</span><span class=operator>-></span><span class=property>now_serving</span>, <span class=number>1</span>, <span class=variable>memory_order_release</span>);
}</code></pre><p>Memory ordering is a super deep field, and I re-learn a lot of it every time I need to use it. Leaving u with one fun thing before I go, read about the <a href=https://www.cs.umd.edu/~pugh/java/memoryModel/AlphaReordering.html>DEC Alpha</a>. It's real cursed :3 <a href=https://en.wikipedia.org/wiki/Consistency_model#Relaxing_read_and_read_to_write_program_orders:_Alpha,_RMO,_and_PowerPC>Wikipedia has more</a> but that previous link is by far the simplest explainer of just how bonkers it is.</p><section class=footnotes data-footnotes><ol><li id=fn-1><p>In a bizarre twist of fate, the non-<code>_explicit</code> version of atomic functions all default to <code>memory_order_seqcst</code>, the most strict ordering. The C standard library having safe defaults?? Improbable. <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-1>↩</a></p></li><li id=fn-2><p>Minus certain definitions of correct, such as actually descheduling blocked threads, detecting misuses of the API, detecting deadlocks, etc. <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-2>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Fun C++ Quiz!]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2223275/" />
      <id>https://wolfgirl.dev/blog/2223275/</id>
      <published>2023-07-27T01:50:46Z</published>
      <updated>2023-07-27T01:50:46Z</updated>
      <summary><![CDATA[In which I demonstrate some truly bizzare C++ ABI behavior.]]></summary>
      <content type="html"><![CDATA[<p>Originally published on Cohost.</p><hr><p>Why doesn't the following code run? (comments represent different files):</p><pre class=syntax-highlighting><code class=language-c++><span class=comment>// foo.hpp</span>
<span class=keyword>template</span> <span class=operator>&lt;</span><span class=keyword>typename</span> <span class=type>T</span><span class=operator>></span>
<span class=keyword>struct</span> <span class=type>Foo</span> {
  <span class=keyword>static</span> <span class=type>void</span> <span class=function>foo</span>(<span class=type>void</span>);
}
<span class=comment>// foo.cpp</span>
<span class=keyword>#include</span> <span class=string>&lt;iostream></span>
<span class=keyword>#include</span> <span class=string>"foo.hpp"</span>
<span class=keyword>template</span> <span class=operator>&lt;></span>
<span class=keyword>struct</span> <span class=type>Foo</span><span class=operator>&lt;</span><span class=type>int</span><span class=operator>></span> {
  <span class=keyword>static</span> <span class=type>void</span> <span class=function>foo</span>() { std::<span class=variable>cout</span> &lt;&lt; <span class=string>"foo\n"</span>; }
}
<span class=comment>// main.cpp</span>
<span class=keyword>#include</span> <span class=string>"foo.hpp"</span>
<span class=type>int</span> <span class=function>main</span>(<span class=type>void</span>) {
  <span class=type>Foo</span><span class=operator>&lt;</span><span class=type>int</span><span class=operator>></span>::<span class=function>foo</span>();
}</code></pre><br><details><summary>Answer</summary> <p>From <a href=https://stackoverflow.com/questions/3989435/why-do-i-get-missing-symbols-for-an-explicit-template-specialization-in-a-static>this SO post</a>. Basically, <code>foo.cpp</code> needs to be</p> <pre class=syntax-highlighting><code class=language-c++><span class=keyword>#include</span> <span class=string>&lt;iostream></span>
<span class=keyword>#include</span> <span class=string>"foo.hpp"</span>
<span class=keyword>template</span> <span class=keyword>struct</span> <span class=type>Foo</span><span class=operator>&lt;</span><span class=type>int</span><span class=operator>></span><span class=variable>;
</span><span class=type>void</span><span class=variable> </span><span class=type>Foo</span><span class=operator>&lt;</span><span class=type>int</span><span class=operator>></span><span class=variable>::</span><span class=function>foo</span><span class=variable>() { std::</span><span class=variable>cout</span><span class=variable> &lt;&lt; </span><span class=string>"foo\n"</span><span class=variable>; }</span></code></pre> <p>because otherwise, member function definitions are secretly <code>inline</code>, and since there is no usage site in the same compilation unit they won't get exported as symbols. Declaring them like this lets them be non-inline, and the symbol can be used in the other compilation unit.</p></details><p>hell language btw.</p>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Exploring Mutexes In C]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2064554/" />
      <id>https://wolfgirl.dev/blog/2064554/</id>
      <published>2023-07-16T18:33:53Z</published>
      <updated>2023-07-16T18:33:53Z</updated>
      
      <content type="html"><![CDATA[<p>Originally published on Cohost under the title "yeah i got nerd-sniped" as a response to someone who posted under the "#c" tag with a simple spinlock implementation.</p><hr><p>i <em>love</em> talking about mutexes so now i'm going to make that everyone else's problem</p><p>ok so. indeed, that code is a relatively straightforward implementation of mutexes. i could digress into stuff about <code>volatile</code> and memory orderings, but instead, there is something more important we need to talk about before considering if this mutex is "good":</p><h2><a aria-hidden=true class=anchor href=#what-is-a-mutex id=heading-what-is-a-mutex></a>What Is A Mutex?</h2><p>By its name, a mutex is something that provides <strong>Mut</strong>ual <strong>Ex</strong>clusion of a shared resource for concurrent tasks. In plain language:</p><blockquote><p>Only one task can access the thing at a time</p></blockquote><p>Programmers call it "acquiring" or "locking" the mutex when you attempt to access that shared resource, and "releasing" or "unlocking" the mutex when you had ownership of the resource and are giving it away for others to use.</p><p>Besides mutual exclusion, there's another property (not in the name) that's required for something to be a proper mutex:</p><h3><a aria-hidden=true class=anchor href=#progress id=heading-progress></a>Progress</h3><blockquote><p>No matter how many tasks are trying to acquire the mutex, if the resource isn't owned, someone will be able to get it</p></blockquote><p>It's hard to mess this one up; in fact I'd say most buggy mutex implementations skew the other way by not correctly providing mutual exclusion. To give a concrete example of how progress could be violated, consider the following implementation: (for some reason i couldn't put it inside a &lt;details>, sorry)</p><pre class=syntax-highlighting><code class=language-c><span class=keyword>#include</span> <span class=string>&lt;stdatomic.h></span>

<span class=keyword>typedef</span> <span class=keyword>struct</span> {
  <span class=type>queue_t</span> <span class=property>waiters</span>;
  <span class=type>atomic_int</span> <span class=property>owner</span>;
} <span class=type>mutex_t</span>;

<span class=keyword>static</span> <span class=keyword>const</span> <span class=type>int</span> <span class=constant>EMPTY</span> <span class=operator>=</span> <span class=number>0</span>;

<span class=type>void</span> <span class=function>lock</span>(<span class=type>mutex_t</span> <span class=operator>*</span><span class=variable>m</span>, <span class=type>int</span> <span class=variable>pid</span>) {
  <span class=keyword>if</span> (!<span class=function>atomic_compare_exchange_strong</span>(<span class=operator>&</span><span class=variable>m</span><span class=operator>-></span><span class=property>owner</span>, <span class=operator>&</span><span class=constant>EMPTY</span>, <span class=variable>pid</span>)) {
    <span class=function>push_back</span>(<span class=operator>&</span><span class=variable>m</span><span class=operator>-></span><span class=property>q</span>, <span class=variable>pid</span>);
    <span class=function>deschedule</span>(); <span class=comment>// Pauses execution until reschedule(pid) called</span>
  }
  <span class=variable>m</span><span class=operator>-></span><span class=property>owner</span> <span class=operator>=</span> <span class=variable>pid</span>;
}

<span class=type>void</span> <span class=function>unlock</span>(<span class=type>mutex_t</span> <span class=operator>*</span><span class=variable>mutex</span>) {
  <span class=type>int</span> <span class=variable>pid</span> <span class=operator>=</span> <span class=function>pop_front</span>(<span class=operator>&</span><span class=variable>m</span><span class=operator>-></span><span class=property>q</span>);
  <span class=keyword>if</span> (<span class=variable>pid</span> <span class=operator>!=</span> <span class=constant>EMPTY</span>) {
    <span class=function>reschedule</span>(<span class=variable>pid</span>);
  } <span class=keyword>else</span> {
    <span class=function>atomic_store</span>(<span class=operator>&</span><span class=variable>m</span><span class=operator>-></span><span class=property>owner</span>, <span class=constant>EMPTY</span>);
  }
}</code></pre><summary>Can you spot an error with this? I'll hide the intended solution in case you want to puzzle it out. <details><p>Between the <code>atomic_compare_exchange_strong</code> line and the <code>push_back</code> line, the current owner could release the mutex without noticing that we were trying to put ourselves on the queue. This violates progress because a single task trying to acquire the lock was not able to.</p> <p>There are certainly many other bugs, such as being able to race on <code>m->q</code> if it is not atomic. That is the thing about bugs, more pop up than u intend :)</details></summary><p>Progress and mutual exclusion are basically required for a correct mutex, but this last one is just nice to have:</p><h3><a aria-hidden=true class=anchor href=#bounded-waiting id=heading-bounded-waiting></a>Bounded Waiting</h3><blockquote><p>If a task tries to acquire the mutex, it will eventually be the owner of the resource</p></blockquote><p>This can be strengthened further by putting bounds on how long it will take (a function of the number of competing tasks, proportional to), or weakened to deal with probabilistic algorithms. In any case, it's common for implementations attempting to go for bounded waiting do so at the cost of progress or mutual exclusion (by accident, as was the case in the example). So it is refreshing to see a simple spinlock that won't even try to deal with it.</p><p>However! Bounded waiting is still a very important property, and any non-specialized implementation should have it. Consider the following code:</p><pre class=syntax-highlighting><code class=language-c><span class=type>void</span> <span class=function>mainloop</span>(<span class=type>void</span>) {
  <span class=keyword>while</span> (!<span class=variable>should_stop</span>) {
    <span class=function>lock</span>(<span class=operator>&</span><span class=variable>m</span>);
    <span class=function>process</span>(<span class=operator>&</span><span class=variable>shared_resource</span>);
    <span class=function>unlock</span>(<span class=operator>&</span><span class=variable>m</span>);
  }
}</code></pre><p>To be clear, this is is a terrible, effectively single-threaded design. Still, it's not all that uncommon in real designs for threads to relatively quickly acquire and release the same mutex over and over, which can starve other threads if there isn't bounded waiting on the mutex.</p><h2><a aria-hidden=true class=anchor href=#can-we-achieve-bounded-waiting-with-only-stdatomich id=heading-can-we-achieve-bounded-waiting-with-only-stdatomich></a>Can We Achieve Bounded Waiting With Only <code>&lt;stdatomic.h></code>?</h2><p>My claim is yes! Here is an example of such a mutex, known as a ticket lock:</p><pre class=syntax-highlighting><code class=language-c><span class=keyword>#include</span> <span class=string>&lt;stdatomic.h></span>

<span class=keyword>typedef</span> <span class=keyword>struct</span> {
  <span class=type>atomic_int</span> <span class=property>next_ticket</span>;
  <span class=type>atomic_int</span> <span class=property>now_serving</span>;
} <span class=type>mutex_t</span>;

<span class=type>void</span> <span class=function>lock</span>(<span class=type>mutex_t</span> <span class=operator>*</span><span class=variable>m</span>) {
  <span class=comment>// Get our ticket</span>
  <span class=type>int</span> <span class=variable>ticket</span> <span class=operator>=</span> <span class=function>atomic_fetch_add</span>(<span class=operator>&</span><span class=variable>m</span><span class=operator>-></span><span class=property>next_ticket</span>, <span class=number>1</span>, <span class=variable>memory_order_relaxed</span>);
  <span class=comment>// Spin until we are being served</span>
  <span class=keyword>while</span> (<span class=variable>ticket</span> <span class=operator>!=</span> <span class=variable>m</span><span class=operator>-></span><span class=property>now_serving</span>) {}
  <span class=function>atomic_thread_fence</span>(<span class=variable>memory_order_acquire</span>);
}

<span class=type>void</span> <span class=function>unlock</span>(<span class=type>mutex_t</span> <span class=operator>*</span><span class=variable>m</span>) {
  <span class=comment>// Let the next ticket be served</span>
  <span class=function>atomic_fetch_add</span>(<span class=operator>&</span><span class=variable>m</span><span class=operator>-></span><span class=property>now_serving</span>, <span class=number>1</span>, <span class=variable>memory_order_release</span>);
}</code></pre><p>How it works is fairly simple, and pretty much entirely explained in the comments already. It's inspired by how you'd wait in line at a deli or pharmacy.</p><p>Some great things about this lock:</p><ul><li>just one atomic operation to lock and unlock</li><li>small (8 bytes)</li></ul><p>Some not-great things about this lock:</p><ul><li>spin waiting :(</li><li>technically, <a href=https://en.wikipedia.org/wiki/ABA_problem>ABA</a>, but with 32-bit tickets this shouldn't be an issue in practice</li><li>doesn't detect misuse</li></ul><p>Most of these deficits could be solved by better design, but i am too lazy rn :P</p><p>Now, you may have still some questions like "wait why didn't you use <code>volatile</code>?" and "what's with that memory order stuff?", but this post is a little long as it stands, so I figure I'll leave the discussion about How Your Compiler And CPU Lie To You for a future post. Or just read <a href=https://faultlore.com/blah/tower-of-weakenings/>Tower Of Weakenings</a> because it answers or has links to answers for those questions and more.</p><p><del>And by the way, it's entirely possible I missed something and this code has a bug I am unaware of, so please do ask questions if you think something is up!</del><br><del>edit: I did get it wrong in multiple ways the first time. After talking with a few friends I am now more confident it is correct :) will explain in the later post</del><br>edit 2: I got it wrong again! Definitely making a post because explaining how I got things wrong is quite a journey.</p>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    </feed>
  