<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Isaac Clayton</title>
    <subtitle>A cozy little corner of the web.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://slightknack.dev/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://slightknack.dev"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-04-06T00:00:00+00:00</updated>
    <id>https://slightknack.dev/atom.xml</id>
    <entry xml:lang="en">
        <title>196 Years</title>
        <published>2026-04-06T00:00:00+00:00</published>
        <updated>2026-04-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-04-06/"/>
        <id>https://slightknack.dev/daily/2026-04-06/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-04-06/">&lt;p&gt;It’s been a while, I know. I sit here writing today because I feel trapped; a prisonless prisoner who laid bricks until one day he woke up in a cell of his own creation.&lt;&#x2F;p&gt;
&lt;p&gt;Am I overconstrained, overburdened, overpromised? In the event that I carry all the parcels I bear through to the date of their respective deliveries, I’m certain I will emerge on the other side a better person. The constraints seek resolution; the burdens are amenable to carrying; the promises sway, flexible but unyielding.&lt;&#x2F;p&gt;
&lt;p&gt;On the other hand: I am afraid who I will have become if I fumble and lose what I have been entrusted. Similarly, if I hold it all too tight, and the weight pins my body to the cool, damp earth, it is certain that against the ground I will remain: the burdens never shed, the destination never reached. Sometimes I ask, “This destination, is it mine?” Am &lt;em&gt;I&lt;&#x2F;em&gt; living out the dream of another?&lt;&#x2F;p&gt;
&lt;p&gt;People ask things of me and I’m terrified to respond. Terrified that I will sign yet another social contract. I am not a busy person, just overwhelmed. I do not feel the satisfaction some seek when telling others “no”. I march through the work I have to do, and it is done, but I have done nothing. What am I doing?&lt;&#x2F;p&gt;
&lt;p&gt;I’m being dramatic. I should eat something. If there’s any week after which to lighten one’s burdens, this one isn’t bad!&lt;&#x2F;p&gt;
&lt;p&gt;Tomorrow is a new day! I will stay my course and fight a good fight; it does not matter that I win, just that I remain: both true to myself, and the trust that others have placed in me. I need to put on my oxygen mask, unswamp my canoe, start the engine, break the curse, turn the tables, put on my socks and shoes, pull up my big-boy pants, because tomorrow is a new day, and the sun won’t wait to rise for those who waste asleep.&lt;&#x2F;p&gt;
&lt;p&gt;In any case, the sunrise is too beautiful to miss.&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;crowprose.com&#x2F;blog&#x2F;competence-as-tragedy&quot;&gt;Competence as Tragedy&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Neopack is live</title>
        <published>2026-03-04T00:00:00+00:00</published>
        <updated>2026-03-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-03-04/"/>
        <id>https://slightknack.dev/daily/2026-03-04/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-03-04/">&lt;p&gt;&lt;em&gt;Slowly emerges from the sixth sea of silence.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Hello! I’m back for the time being.&lt;&#x2F;p&gt;
&lt;p&gt;I started working on a handful of projects, and those have been taking up my blogging time. Sorry for the inconsistency.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to announce that I’ve published the first (and hopefully only) version of Neopack! Neopack is a very small library for quick and schema-agnostic tag-length-value serialization. It should be good for both IDL and RPC type workloads. Here’s a minimal “getting-started”-type example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;dependencies&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;neopack &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;version &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;1.2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;features &lt;&#x2F;span&gt;&lt;span&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;use neopack::{Pack, Unpack};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(Pack, Unpack)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Point &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;y&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32 &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; bytes = Point { x: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, y: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;}.pack_to_vec().unwrap();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; point = Point::unpack_from_bytes(&amp;amp;bytes).unwrap();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Neopack is what I’d consider to be “done” software. There might be a few minor wrinkles to iron out, but I don’t expect anything major about the library, its API, or the wire format to change anytime soon.&lt;&#x2F;p&gt;
&lt;p&gt;If you’d like to take a look, here are the docs. It is very similar to &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;serde.rs&#x2F;&quot;&gt;serde&lt;&#x2F;a&gt; (except it’s only about one kiloline of code) and has zero-copy deserialization:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;neopack&lt;&#x2F;code&gt; &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;neopack&quot;&gt;on docs.rs&lt;&#x2F;a&gt;. Core library, exposes &lt;code&gt;derive&lt;&#x2F;code&gt; feature.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;neopack-derive&lt;&#x2F;code&gt; &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;neopack-derive&quot;&gt;on docs.rs&lt;&#x2F;a&gt;. Derive macros for the &lt;code&gt;Pack&lt;&#x2F;code&gt; and &lt;code&gt;Unpack&lt;&#x2F;code&gt; traits.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;One limitation of neopack is that it’s &lt;code&gt;no-std&lt;&#x2F;code&gt;. So the crate doesn’t implement serialization for standard library types, like &lt;code&gt;PathBuf&lt;&#x2F;code&gt;. I considered going the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;serde.rs&#x2F;&quot;&gt;serde&lt;&#x2F;a&gt; route, with the fancy &lt;code&gt;mod&lt;&#x2F;code&gt; for custom serialize&#x2F;deserialize, but in the name of simplicity I ask that you serialize these types yourself to bytes or a &lt;code&gt;String&lt;&#x2F;code&gt;, and to not include them in serialized structs. If I figure out a solution that makes everyone happy, I’ll do it!&lt;&#x2F;p&gt;
&lt;p&gt;That’s all! Thanks for reading.&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;journal.stuffwithstuff.com&#x2F;2023&#x2F;08&#x2F;04&#x2F;representing-heterogeneous-data&quot;&gt;Bob Nystrom, Representing Heterogeneous Data&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I enjoyed Bob Nystrom’s musings on designing a language with heterogeneous data types, so you might enjoy it too.&lt;&#x2F;p&gt;
&lt;p&gt;(Neopack is built on heterogeneous Algebraic Data Types. Because neopack uses TLV for everything, you can fully and dynamically inspect and decode a packed value with an unknown schema! Isn’t that neat?)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Programming as theory building is true now more than ever</title>
        <published>2026-02-21T00:00:00+00:00</published>
        <updated>2026-02-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-21/"/>
        <id>https://slightknack.dev/daily/2026-02-21/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-21/">&lt;p&gt;&lt;span class=aside&gt;These past two days have been something of a whirlwind, as I made a couple of important life decisions. The decisions were, in a strange way, very small, but in a “marble rolling down a saddle” sort-of way that might compound and drastically change the side of the saddle my timeline will spill down… we shall see!&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I’ve repeatedly brought up the paper &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pages.cs.wisc.edu&#x2F;~remzi&#x2F;Naur.pdf&quot;&gt;&lt;em&gt;Programming as Theory Building&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; in conversation with friends this past week, so I figured it would be good to write up the common thread of these conversations and discuss how the ideas in the paper are relevant today.&lt;&#x2F;p&gt;
&lt;p&gt;The paper was written in 1985 by Peter Naur. The main thrust of the argument is that when you program, you are doing two things:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Producing the artifact of text, or source code, on disk.&lt;&#x2F;li&gt;
&lt;li&gt;Producing the model of a system in your mind. This is “theory building”.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Programming, Naur argues, should be framed as theory building. The real value of what is produced is precisely the model that lives in the programmer’s mind. The mental model is what allows you to debug, extend, and collaborate on a codebase; not the source code.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;why-the-theory-building-angle-is-useful&quot;&gt;Why the theory building angle is useful&lt;&#x2F;h1&gt;
&lt;p&gt;I really like the way a few insights that naturally follow as a consequence of thinking of programming as theory building. I’ll run through them now.&lt;&#x2F;p&gt;
&lt;p&gt;First, a &lt;em&gt;bug&lt;&#x2F;em&gt; occurs when your mental model of the system’s behavior disagrees with what the code does. As programs do exactly what you write down, a bug isn’t &lt;em&gt;wrong code&lt;&#x2F;em&gt;; it’s doing exactly what you wrote. A bug happens when your code doesn’t match your theory, or your theory doesn’t match the code. Fixing a bug, then, requires either refining your mental model or refining the code. Often it’s both. &lt;span class=aside&gt;In this sense, to a customer, if an app doesn’t work the way they expect it to, that’s a &lt;em&gt;bug&lt;&#x2F;em&gt;, to them. UX bugs occur when we do a poor job of theory building with our user.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Second, source code is a medium of reflection against which further theory can be built. When you have an abstract notion of a system, you can only hold so much of the theory in your head, or in design documents. Producing source code grounds theory, and allows the programmer to explore and discover the right way to extend their existing theories, so new systems can be developed and features can be implemented.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, theory building provides a good framework to think about working on code with others. As the source code is not the artifact, and rather the theory is, a lot of programming &lt;em&gt;on a team&lt;&#x2F;em&gt; boils down to communicating the theory you have built up, and asking good questions to learn the theory your teammates have built up. Different team members hold expert theories for different parts of the codebase. Becase of this, &lt;em&gt;interfaces&lt;&#x2F;em&gt; are clean boundaries where you can communicate a crisp and neatly-packaged theory to a teammate, which packages complexity to become a sharp tool with which to carve whatever code they need to write. Documentation, pair programming, demonstrations are all means through which a body of theory is built &lt;em&gt;and&lt;&#x2F;em&gt; communicated.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pair-programming-is-great-for-theory-building&quot;&gt;Pair programming is great for theory building&lt;&#x2F;h1&gt;
&lt;p&gt;In high school, I wrote a lot of code on my own. The first time I really had the opportunity to work on a team on projects with a bus factor bigger than one was when I was an intern at &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;zed.dev&quot;&gt;Zed&lt;&#x2F;a&gt;, in 2022. I spent a lot of time pair programming with Antonio, who taught me a lot about Rust and software engineering.&lt;&#x2F;p&gt;
&lt;p&gt;I now realize that Antonio spent so much time pair programming with me because he was very invested in theory building: both in improving my understanding of the theory behind the editor and the tools we were using, and in improving his understanding of the code I was writing, so he could carry the theory we built together forward after my internship ended.&lt;&#x2F;p&gt;
&lt;p&gt;I’m really grateful for this mentorship and I still think back to a lot of the lessons I learned about e.g. async Rust minutiae Antonio patiently taught me along the way.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;vibecoding-is-antithetical-to-theory-building&quot;&gt;Vibecoding is antithetical to theory building&lt;&#x2F;h1&gt;
&lt;p&gt;Now, the AI tie in. Great. Sure. I saw it coming from a mile away. Here it is: Sometimes AI tools can produce a better code &lt;em&gt;artifact&lt;&#x2F;em&gt;; however, better code that was &lt;em&gt;generated&lt;&#x2F;em&gt; doesn’t help &lt;em&gt;you&lt;&#x2F;em&gt;, as the programmer, to build a better &lt;em&gt;theory&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Taking a step back, the idea of programming as theory building doesn’t materially change because of AI tools that write code now exist. (A follow-on paper that thoroughly investigates the effects of AI tooling on theory building would be interesting to read.) The issue is that it’s easy to convince yourself that you’ve built theory when you haven’t: It’s easy to fall into the trap or classical fantasy of “texting your nerdy eager-to-please friend an app idea and watching it get written”.&lt;&#x2F;p&gt;
&lt;p&gt;First, if you want to be engaged in building theory when using tools, you need to be engaged. If you treat tools as teammates, they will invariably go off and build theory on their own. The model will not go out of its way to teach you its assumptions. You need to understand the theory that has been generated if you wish to extend the artifact of the code.&lt;&#x2F;p&gt;
&lt;p&gt;Second, AI tools have no qualms picking interfaces and invariants for you. The interfaces they choose will often be those interfaces that are the most plausible for what you are trying to do. However, these interfaces are not always the &lt;em&gt;best&lt;&#x2F;em&gt; interfaces for whatever it is that you are specifically working on. Sometimes they’re worse. Sometimes they’re better! Regardless of ontological merit, any interface sucks if you don’t understand it.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, for both of these issues, it is not enough to simply read and write more documentation, as Naur argued back in 1985. Documentation is a poor substitute for theory. AI generated documentation for AI generated code does not necessarily build the right theory in your mind.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;lessons-for-collaborating-on-code&quot;&gt;Lessons for collaborating on code&lt;&#x2F;h1&gt;
&lt;p&gt;With these problems in mind, what can we do? I write here in the context of AI tools that write code, but this is true of collaboration with human teammates as well. (A lot of these ideas are lessons I indirectly learned through Antonio, though we were not using AI generated code at the time.) I’ve learned a few things:&lt;&#x2F;p&gt;
&lt;p&gt;First, you should really care about good, pedagogical documentation and good API design. When you design an interface, you have the opportunity to both build your theory of the system &lt;em&gt;and&lt;&#x2F;em&gt; specify the exact interfaces and invariants you require (instead of letting a teammate, or some &lt;em&gt;other&lt;&#x2F;em&gt; collaborator, choose for you). I find that writing system documentation, writing out types, writing an API, writing property-based tests &lt;em&gt;by hand, before&lt;&#x2F;em&gt; letting an AI implement what is left, produces better results.&lt;&#x2F;p&gt;
&lt;p&gt;Second, you should pay &lt;em&gt;extra attention and slow down&lt;&#x2F;em&gt; when you realize your theory for something you are working on is slipping. When working with AI collaborators, it is easy for experienced engineers to vibe-code a simple app, because the theory is something they already understand fully. The issue arises when a programmer &lt;em&gt;then&lt;&#x2F;em&gt; starts working on extending or developing new aspects of the system &lt;em&gt;without&lt;&#x2F;em&gt; having the &lt;a href=&quot;https:&#x2F;&#x2F;slightknack.dev&#x2F;daily&#x2F;2026-02-18&#x2F;&quot;&gt;symbolic grounding&lt;&#x2F;a&gt; to close that feedback loop, and develop the theory you require.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, you can always ask these tools, as you would a colleague, to be watchful of when your theory is slipping, and to help you understand what you’re missing. AI tools can generate lots of ungrounded theory, yes, but they can also work to answer questions and determine the correctness of your existing theories.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;one-last-note&quot;&gt;One last note&lt;&#x2F;h1&gt;
&lt;p&gt;Importantly, while talking about theory building in the context of AI tools, I should mention that I believe it to be bad to “go off to build thought palaces alone”. Humans communicate differently with other people than they communicate with AI. Having real-life people care about what you’re working on, anxiously engaged with your code, provides the human perspective you miss out on when writing software alone (or with a tool).&lt;&#x2F;p&gt;
&lt;p&gt;That’s it for today! I probably won’t blog tomorrow because it’s Sunday. If you’d like a story about someone who wrote no code but was great at collaborative theory building, you should read:&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dannorth.net&#x2F;blog&#x2F;the-worst-programmer&quot;&gt;The Worst Programmer I Know&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Symbolic grounding</title>
        <published>2026-02-18T00:00:00+00:00</published>
        <updated>2026-02-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-18/"/>
        <id>https://slightknack.dev/daily/2026-02-18/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-18/">&lt;p&gt;After &lt;a href=&quot;&#x2F;daily&#x2F;2026-02-17&#x2F;&quot;&gt;yesterday’s&lt;&#x2F;a&gt; massive post (and given the two assignments I have to do today) I figured I would write something a little shorter today. A little observation, perhaps.&lt;&#x2F;p&gt;
&lt;p&gt;In the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Symbolic_artificial_intelligence&quot;&gt;70s and 80s&lt;&#x2F;a&gt;, people cared a lot about symbolic reasoning; precise relational systems of facts and rules used to derive logical truths. Any relational system is one concerned with resolving constraints. These ideas fell out of favor because of the powerful general “statistical” methods we have today.&lt;&#x2F;p&gt;
&lt;p&gt;From casual usage, I’ve found that writing code with language models pairs very well with property-based testing. Give the model a list of invariants and constraints, along with types and interface definitions, have it generate a ream of tests using a property-based testing library.&lt;&#x2F;p&gt;
&lt;p&gt;Verify the tests are correct, then let the model run in a loop against the &lt;em&gt;symbolically grounded&lt;&#x2F;em&gt; system until all constraints are satisfied. You can even tell it to keep track of the search tree in a consistent format as it goes, to know what to try next and where to backtrack to.&lt;&#x2F;p&gt;
&lt;p&gt;This turns a fuzzy word problem into an unyielding semantic one; the model acts as a very good prior for performing symbolic search.&lt;&#x2F;p&gt;
&lt;p&gt;We’re seeing moves in this direction as newer models employ thousands of little “tool calls” while reasoning, to symbolically ground their reasoning. These systems are trained end-to-end; one day models may be little more than good priors from which to sample belief networks on the fly.&lt;&#x2F;p&gt;
&lt;p&gt;I think datalog or relations generally should be built into programming languages. One language does this well:&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;flix.dev&#x2F;&quot;&gt;The Flix Programming Language&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Epistemic and Aleatoric Uncertainty</title>
        <published>2026-02-17T00:00:00+00:00</published>
        <updated>2026-02-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-17/"/>
        <id>https://slightknack.dev/daily/2026-02-17/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-17/">&lt;p&gt;This is a follow-on post to &lt;del&gt;yesterday’s&lt;&#x2F;del&gt;, um, last Friday’s post about &lt;a href=&quot;&#x2F;daily&#x2F;2026-02-13&#x2F;&quot;&gt;&lt;em&gt;curiosity&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; driven approaches to reinforcement learning. &lt;span class=aside&gt;I intended to publish this post on the 14th, but it was Valentine’s day weekend and $\text{irl} &amp;gt; \text{blog}$.&lt;&#x2F;span&gt; To summarize:&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;Imagine we have some black-box environment with a state space $s$. At each timestep, we receive an observation $o$ (derived from $s$), and can pick an action $a$. The environment evolves according to a stochastic state transition dynamic $s’ \sim P(\cdot \mid s, a)$ depending on the current state and our action, producing a new observation $o’$.&lt;&#x2F;p&gt;
&lt;p&gt;Suppose we don’t have a goal or reward function for our environment; we simply would like to train a neural network to model $P(s’ \mid s, a)$. How can we sample sequences of actions, or &lt;em&gt;episodes&lt;&#x2F;em&gt;, that contain all the information we need to build an accurate model of state transition dynamics?&lt;&#x2F;p&gt;
&lt;p&gt;The solution looks something like &lt;em&gt;curiosity&lt;&#x2F;em&gt;: construct a reward signal along the lines of “the number of learnable bits of information per observation”, and train a policy $\pi$ using traditional RL techniques like PPO to improve the policy and sample divers information-dense trajectories.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Today I’m going to try to explain the following relation:&lt;&#x2F;p&gt;
&lt;p&gt;$$\text{uncertainty} = \text{aleatoric} + \text{epistemic}$$&lt;&#x2F;p&gt;
&lt;p&gt;If that doesn’t make sense now, don’t worry. It should be clear by the end of the post.&lt;&#x2F;p&gt;
&lt;p&gt;Our goal is to learn a &lt;em&gt;policy&lt;&#x2F;em&gt; that is capable of exploring an arbitrary environment. In essence, we want to seek out states that are surprising, but not totally random; in other words, we want to find states where we are uncertain, but capable of learning through interaction and observation.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;surprisal&quot;&gt;Surprisal&lt;&#x2F;h1&gt;
&lt;p&gt;Let’s start by quantifying this idea of &lt;em&gt;surprise&lt;&#x2F;em&gt;. In information theory, there is this concept of &lt;em&gt;surprisal&lt;&#x2F;em&gt;, which is defined as:&lt;&#x2F;p&gt;
&lt;p&gt;$$h(y) = -\log P(y)$$&lt;&#x2F;p&gt;
&lt;p&gt;Here, $y \sim Y$ is some specific outcome; an event sampled from a (discrete) random variable $Y$. Surprisal $h(y)$ is measured in &lt;em&gt;bits&lt;&#x2F;em&gt;. A likely outcome can be encoded in few bits, and likewise tells you little; an unlikely outcome takes many bits to encode, and conversely tells you a lot.&lt;&#x2F;p&gt;
&lt;p&gt;To make surprisal concrete, rolling a 4 on a fair die has probability $\frac{1}{6}$. The surprisal is $h(⚃) = \log_2 6 \approx 2.58$ bits of information.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;entropy-total-uncertainty&quot;&gt;Entropy, total uncertainty&lt;&#x2F;h1&gt;
&lt;p&gt;Surprisal is concerned with a single event. If we have a distribution $Y$ over multiple events, is there a way to describe how much information we expect to learn upon measuring the outcome?&lt;&#x2F;p&gt;
&lt;p&gt;Yes, and this is exactly &lt;em&gt;entropy&lt;&#x2F;em&gt; $H[Y]$, or “expected surprisal”:&lt;&#x2F;p&gt;
&lt;p&gt;$$H[Y] = \mathbb{E}[-\log P(Y)]$$&lt;&#x2F;p&gt;
&lt;p&gt;We use lowercase $h(y)$ for the surprisal of a single event $y$, and uppercase $H[Y]$ for the surprisal over a distribution of events $Y$. The expectation $\mathbb{E}$ here just measures the surprisal of each event, weighted by how probable it is. Mathematically speaking:&lt;&#x2F;p&gt;
&lt;p&gt;$$H[Y] = \sum_y P(y) \cdot h(y)$$&lt;&#x2F;p&gt;
&lt;p&gt;You can think about entropy as, “in expectation, how many bits of information will we gain by making an observation?”&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conditional-entropy&quot;&gt;Conditional entropy&lt;&#x2F;h1&gt;
&lt;p&gt;Entropy measures how much information we expect to learn about $Y$… in isolation. Oftentimes, we’ve observed some correlated event $x$. In that case, how much information do we expect to learn from $Y$, given we already know $x$? This is precisely &lt;em&gt;conditional entropy&lt;&#x2F;em&gt; $H[Y \mid x]$:&lt;&#x2F;p&gt;
&lt;p&gt;$$H[Y \mid x] = \mathbb{E}[-\log P(Y \mid x)]$$&lt;&#x2F;p&gt;
&lt;p&gt;&lt;span class=aside&gt;We write lowercase $x$ because we’re conditioning on a specific input, rather than averaging over &lt;em&gt;all&lt;&#x2F;em&gt; possible inputs, which is what you normally see.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Note that this is exactly the same thing as entropy, just over the conditional distribution $P(Y \mid x)$ instead of $P(Y)$. If knowing $x$ makes $Y$ easier to predict, the conditional entropy is lower. If $x$ is independent of $Y$, then $H[Y \mid x] = H[Y]$. Like entropy, we can also write conditional entropy as a weighted sum, but conditioned on $x$:&lt;&#x2F;p&gt;
&lt;p&gt;$$H[Y \mid x] = \sum_y P(y \mid x) \cdot h(y \mid x)$$&lt;&#x2F;p&gt;
&lt;p&gt;In our environment, if $S$ is the distribution over next states, and $(s, a)$ is our state action pair, then $H[S \mid s, a]$ is an exact measure of how unpredictable the next state is from a given state-action pair. This measures, in essence, how random state transitions are. For a deterministic environment, $H[S \mid s, a] = 0$. &lt;span class=aside&gt;In practice, we observe $o$, not $s$. Reconstructing state from observations is a different &lt;em&gt;filtery, hidden-markov-modelly&lt;&#x2F;em&gt; can of worms. We’ll gloss over this for now, and condition on $s$ directly, but the intractable component of epistemic uncertainty I touch on later comes partly from this gap.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Conditional entropy tells us how uncertain $Y$ is &lt;em&gt;given&lt;&#x2F;em&gt; a specific event $x$. But in general, if we know the value of a correlated random variable $Z$, we often want to ask how much of $Y$ it tells us. If measured in bits, this quantity is called &lt;em&gt;mutual information&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;mutual-information&quot;&gt;Mutual information&lt;&#x2F;h1&gt;
&lt;p&gt;A coin flip has high conditional entropy regardless of context; you can never learn to predict it. For curiosity, we want our policy to seek states that it finds unpredictable because it &lt;em&gt;has not yet seen them&lt;&#x2F;em&gt;, not because the environment is random and can’t be predicted.&lt;&#x2F;p&gt;
&lt;p&gt;Mutual information between $Y$ and some other variable $Z$ measures how many bits of the expected surprisal of $Y$ are accounted for if you already know $Z$. It’s precisely the gap between entropy and conditional entropy:&lt;&#x2F;p&gt;
&lt;p&gt;$$I[Y; Z] = H[Y] - H[Y \mid Z]$$&lt;&#x2F;p&gt;
&lt;p&gt;We can draw this as a Venn diagram, which is nicely symmetric:&lt;&#x2F;p&gt;
&lt;!--
date: 2026-02-17
model: claude-opus-4-6
driver: Isaac Clayton
note: No prose on this website is generated, this is provenance for the diagram that follows:
--&gt;
&lt;div class=&quot;venn&quot; id=&quot;venn-entropy&quot;&gt;
&lt;style&gt;
.venn {
  width: 100%;
  margin: 20pt 0;
}
.venn .venn-titles {
  display: flex;
  justify-content: space-around;
  padding: 0 10%;
  margin-bottom: 4px;
}
.venn .venn-body {
  position: relative;
}
.venn svg {
  width: 100%;
  height: auto;
  display: block;
}
.venn .venn-svg-v { display: none; }
.venn .venn-labels {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}
.venn .venn-label {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.venn .venn-label-soft { color: var(--fill-text); }
.venn .venn-label-accent { color: var(--fill-bg); }
.venn .venn-titles-bottom { display: none; }
@media only screen and (max-width: 900px) {
  .venn .venn-titles { padding: 0; justify-content: center; }
  .venn .venn-title-second { display: none; }
  .venn .venn-titles-bottom { display: flex; justify-content: center; margin-top: 4px; }
  .venn .venn-svg-h { display: none; }
  .venn .venn-svg-v { display: block; max-height: 70vh; margin: 0 auto; }
  .venn .venn-labels {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr 1fr 1fr;
  }
}
&lt;&#x2F;style&gt;
&lt;div class=&quot;venn-titles&quot;&gt;
&lt;span&gt;$H[Y]$&lt;&#x2F;span&gt;
&lt;span class=&quot;venn-title-second&quot;&gt;$H[Z]$&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;venn-body&quot;&gt;
&lt;svg class=&quot;venn-svg-h&quot; viewBox=&quot;53 0 294 200&quot; xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot;&gt;
&lt;defs&gt;
&lt;clipPath id=&quot;ve-clip-left-h&quot;&gt;&lt;circle cx=&quot;151&quot; cy=&quot;100&quot; r=&quot;98&quot;&#x2F;&gt;&lt;&#x2F;clipPath&gt;
&lt;&#x2F;defs&gt;
&lt;circle cx=&quot;151&quot; cy=&quot;100&quot; r=&quot;98&quot; fill=&quot;var(--fill-soft)&quot;&#x2F;&gt;
&lt;circle cx=&quot;249&quot; cy=&quot;100&quot; r=&quot;98&quot; fill=&quot;var(--fill-soft)&quot;&#x2F;&gt;
&lt;circle cx=&quot;249&quot; cy=&quot;100&quot; r=&quot;98&quot; clip-path=&quot;url(#ve-clip-left-h)&quot; fill=&quot;var(--fill-accent)&quot;&#x2F;&gt;
&lt;&#x2F;svg&gt;
&lt;svg class=&quot;venn-svg-v&quot; viewBox=&quot;0 53 200 294&quot; xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot;&gt;
&lt;defs&gt;
&lt;clipPath id=&quot;ve-clip-top-v&quot;&gt;&lt;circle cx=&quot;100&quot; cy=&quot;151&quot; r=&quot;98&quot;&#x2F;&gt;&lt;&#x2F;clipPath&gt;
&lt;&#x2F;defs&gt;
&lt;circle cx=&quot;100&quot; cy=&quot;151&quot; r=&quot;98&quot; fill=&quot;var(--fill-soft)&quot;&#x2F;&gt;
&lt;circle cx=&quot;100&quot; cy=&quot;249&quot; r=&quot;98&quot; fill=&quot;var(--fill-soft)&quot;&#x2F;&gt;
&lt;circle cx=&quot;100&quot; cy=&quot;249&quot; r=&quot;98&quot; clip-path=&quot;url(#ve-clip-top-v)&quot; fill=&quot;var(--fill-accent)&quot;&#x2F;&gt;
&lt;&#x2F;svg&gt;
&lt;div class=&quot;venn-labels&quot;&gt;
&lt;div class=&quot;venn-label venn-label-soft&quot;&gt;$H[Y \mid Z]$&lt;&#x2F;div&gt;
&lt;div class=&quot;venn-label venn-label-accent&quot;&gt;$I[Y; Z]$&lt;&#x2F;div&gt;
&lt;div class=&quot;venn-label venn-label-soft&quot;&gt;$H[Z \mid Y]$&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;venn-titles venn-titles-bottom&quot;&gt;
&lt;span&gt;$H[Z]$&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Consider the limiting cases: if $Z$ tells us everything about $Y$, then $H[Y \mid Z] = 0$ and $I[Y; Z] = H[Y]$. All of $Y$’s information is already contained in $Z$; the circles fully overlap. On the other hand, if $Y$ and $Z$ are independent, $Z$ tells us nothing about $Y$, so $I[Y; Z] = 0$, and the circles are disjoint.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;aleatoric-uncertainty&quot;&gt;Aleatoric uncertainty&lt;&#x2F;h1&gt;
&lt;p&gt;Up until this point, we have been talking about information abstractly. With surprisal, (conditional) entropy, and mutual information, we have the tools to quantify &lt;em&gt;how much&lt;&#x2F;em&gt; we learn from an observation, and what it means for a state transition to be “random”.&lt;&#x2F;p&gt;
&lt;p&gt;In statistics, we talk about &lt;em&gt;models&lt;&#x2F;em&gt;. Models have parameters, often denoted $\theta$, and let us make predictions through inference. (Note that the model $\theta$ is distinct from the policy $\pi$. The model predicts &lt;em&gt;state transitions&lt;&#x2F;em&gt;, and the policy decides &lt;em&gt;actions&lt;&#x2F;em&gt;. We train $\pi$ to seek states where $\theta$ can learn.) &lt;span class=aside&gt;In practice, you often train them together, so the world model is contained either implicitly or explicitly in the policy.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Concretely, with a model $\theta$ we can ask: Given an input $x$, what &lt;em&gt;distribution&lt;&#x2F;em&gt; does the model assign to the outcome $Y$? We can write this as $P(Y = y \mid x, \theta)$, for the probability of a specific outcome $y$.&lt;&#x2F;p&gt;
&lt;p&gt;We can also talk about &lt;em&gt;uncertainty&lt;&#x2F;em&gt;. Given specific parameters $\theta$ and an input $x$, we can quantify the uncertainty of the outcome $Y$ using conditional entropy:&lt;&#x2F;p&gt;
&lt;p&gt;$$\text{prediction uncertainty} = H[Y \mid x, \theta]$$&lt;&#x2F;p&gt;
&lt;p&gt;This “prediction uncertainty” is the noise that the model can never explain. If we had a perfect model of a deterministic process, $H[Y \mid x, \theta] = 0$. However, even if we have a perfect model $\theta^*$ for a random, or stochastic, process, by virtue of being stochastic, $H[Y \mid x, \theta] &amp;gt; 0$. We can never predict a random outcome with 100% certainty.&lt;&#x2F;p&gt;
&lt;p&gt;In real life, we don’t have perfect models like $\theta^*$. We derive parameters $\theta$ for models from data, $D$, a set of observations. In practice, we often find $\theta$ by randomly initializing a model and training it on $D$. &lt;span class=aside&gt;Training through, for example, stochastic gradient descent.&lt;&#x2F;span&gt; We could end up with many different parameters $\theta$ depending on how the model is initialized. Given a model class, training on $D$ gives us a &lt;em&gt;posterior&lt;&#x2F;em&gt; distribution over all models, which we can write $P(\theta \mid D)$. In English, what is the distribution over model parameters $\theta$, given the training data $D$ we have?&lt;&#x2F;p&gt;
&lt;p&gt;Different models make different predictions, but no model can explain away noise. We want to quantify this irreducible noise. Since we don’t know which model is correct, the natural way is to average prediction uncertainty over the posterior $P(\theta \mid D)$:&lt;&#x2F;p&gt;
&lt;p&gt;$$\text{aleatoric} = \mathbb{E}_{P(\theta \mid D)}[H[Y \mid x, \theta]]$$&lt;&#x2F;p&gt;
&lt;p&gt;This is the noise floor, the uncertainty that remains no matter how much data we collect or how well we train. This quantity is called &lt;em&gt;aleatoric uncertainty&lt;&#x2F;em&gt;. &lt;span class=aside&gt;In Portuguese, the word for “random” is &lt;em&gt;aleatório&lt;&#x2F;em&gt;, so that’s how I remember “aleatoric”.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If we take $H[Y \mid x, \theta]$ and marginalize out $\theta$, we are left with $H[Y \mid x]$, our uncertainty over $Y$ given $x$. There is this gap, then, between total uncertainty $H[Y \mid x]$ and aleatoric uncertainty $\mathbb{E}[H[Y \mid x, \theta]]$. The gap is the uncertainty that comes from &lt;em&gt;not knowing which model is correct&lt;&#x2F;em&gt;. This is the uncertainty &lt;em&gt;in the model&lt;&#x2F;em&gt; that will shrink with more of the right data.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;epistemic-uncertainty&quot;&gt;Epistemic uncertainty&lt;&#x2F;h1&gt;
&lt;p&gt;Uncertainty can come from two places: our environment can be noisy, or our model can be incorrect. We just derived &lt;em&gt;aleatoric uncertainty&lt;&#x2F;em&gt;, which quantifies how noisy our environment is. How do we know when our model is uncertain?&lt;&#x2F;p&gt;
&lt;p&gt;If we take our total uncertainty $H[Y \mid x]$ and we subtract out the aleatoric uncertainty $\mathbb{E}[H[Y \mid x, \theta]]$, we are left with a gap. This gap is all the uncertainty that is not explained by noise. This gap is precisely how uncertain our model is:&lt;&#x2F;p&gt;
&lt;p&gt;$$\text{epistemic} = H[Y \mid x] - \mathbb{E}_{P(\theta \mid D)}[H[Y \mid x, \theta]]$$&lt;&#x2F;p&gt;
&lt;p&gt;Those who were paying attention earlier might have noticed that this quantity, &lt;em&gt;epistemic uncertainty&lt;&#x2F;em&gt; is exactly the &lt;em&gt;mutual information&lt;&#x2F;em&gt; between the outcome $Y$ and model parameters $\theta$, conditioned on our training data $D$ and our input $x$:&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{align}
I[Y; \theta \mid x, D] &amp;amp;= H[Y \mid x] - \mathbb{E}_{P(\theta \mid D)}[H[Y \mid x, \theta]] \\
\text{epistemic} &amp;amp;= \text{total} - \text{aleatoric}
\end{align}$$&lt;&#x2F;p&gt;
&lt;p&gt;Another way to think about this: if the models in the posterior $P(\theta \mid D)$  disagree on what $Y$ will be given the training data $D$ we have, epistemic uncertainty is high.&lt;&#x2F;p&gt;
&lt;p&gt;If we collect more training data in states where epistemic uncertainty is high, we can reduce it. More data shrinks the disagreement of the posterior. As the distribution of possible models converges, $I[Y; \theta \mid x, D] \to 0$, epistemic uncertainty decreases.&lt;&#x2F;p&gt;
&lt;p&gt;Curiosity should target states of high epistemic uncertainty. Our goal is to build a dataset $D$ on which we can train a model $\theta$ that accurately models the state transition dynamics of the environment. To do this, we want to learn a policy $\pi$ that can navigate to regions of state space that are hard to reach, and explore all there is to learn.&lt;&#x2F;p&gt;
&lt;p&gt;If we train a policy to maximize epistemic uncertainty over an episode, we are training the policy to maximize the time it spends in states when the model can still learn.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;decomposition&quot;&gt;Decomposition&lt;&#x2F;h1&gt;
&lt;p&gt;Now we have all the tools to understand the original equation:&lt;&#x2F;p&gt;
&lt;p&gt;$$\text{uncertainty} = \text{aleatoric} + \text{epistemic}$$&lt;&#x2F;p&gt;
&lt;p&gt;Where &lt;em&gt;uncertainty&lt;&#x2F;em&gt; $H[Y \mid x]$ is the total uncertainty over outcomes given the input, measured using conditional entropy. &lt;em&gt;Aleatoric uncertainty&lt;&#x2F;em&gt; $\mathbb{E}_{P(\theta \mid D)}[H[Y \mid x, \theta]]$ is the irreducible noise floor, what we can never learn through observation. &lt;em&gt;Epistemic uncertainty&lt;&#x2F;em&gt; $I[Y; \theta \mid x, D]$ is the mutual information between outcomes and model parameters.&lt;&#x2F;p&gt;
&lt;p&gt;We can make this relationship explicit by drawing it on the mutual information Venn diagram from earlier:&lt;&#x2F;p&gt;
&lt;!--
date: 2026-02-17
model: claude-opus-4-6
driver: Isaac Clayton
note: No prose on this website is generated, this is provenance for the diagram that follows:
--&gt;
&lt;div class=&quot;venn&quot; id=&quot;venn-decomposition&quot;&gt;
&lt;style&gt;
.venn {
  width: 100%;
  margin: 20pt 0;
}
.venn .venn-titles {
  display: flex;
  justify-content: space-around;
  padding: 0 10%;
  margin-bottom: 4px;
}
.venn .venn-body {
  position: relative;
}
.venn svg {
  width: 100%;
  height: auto;
  display: block;
}
.venn .venn-svg-v { display: none; }
.venn .venn-labels {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}
.venn .venn-label {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.venn .venn-label-soft { color: var(--fill-text); }
.venn .venn-label-accent { color: var(--fill-bg); }
.venn .venn-titles-bottom { display: none; }
@media only screen and (max-width: 900px) {
  .venn .venn-titles { padding: 0; justify-content: center; }
  .venn .venn-title-second { display: none; }
  .venn .venn-titles-bottom { display: flex; justify-content: center; margin-top: 4px; }
  .venn .venn-svg-h { display: none; }
  .venn .venn-svg-v { display: block; max-height: 70vh; margin: 0 auto; }
  .venn .venn-labels {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr 1fr 1fr;
  }
}
&lt;&#x2F;style&gt;
&lt;div class=&quot;venn-titles&quot;&gt;
&lt;span&gt;$H[Y \mid x]$&lt;&#x2F;span&gt;
&lt;span class=&quot;venn-title-second&quot;&gt;$H[\theta \mid D]$&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;venn-body&quot;&gt;
&lt;svg class=&quot;venn-svg-h&quot; viewBox=&quot;53 0 294 200&quot; xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot;&gt;
&lt;defs&gt;
&lt;clipPath id=&quot;vd-clip-left-h&quot;&gt;&lt;circle cx=&quot;151&quot; cy=&quot;100&quot; r=&quot;98&quot;&#x2F;&gt;&lt;&#x2F;clipPath&gt;
&lt;&#x2F;defs&gt;
&lt;circle cx=&quot;151&quot; cy=&quot;100&quot; r=&quot;98&quot; fill=&quot;var(--fill-soft)&quot;&#x2F;&gt;
&lt;circle cx=&quot;249&quot; cy=&quot;100&quot; r=&quot;98&quot; fill=&quot;var(--fill-soft)&quot;&#x2F;&gt;
&lt;circle cx=&quot;249&quot; cy=&quot;100&quot; r=&quot;98&quot; clip-path=&quot;url(#vd-clip-left-h)&quot; fill=&quot;var(--fill-accent)&quot;&#x2F;&gt;
&lt;&#x2F;svg&gt;
&lt;svg class=&quot;venn-svg-v&quot; viewBox=&quot;0 53 200 294&quot; xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot;&gt;
&lt;defs&gt;
&lt;clipPath id=&quot;vd-clip-top-v&quot;&gt;&lt;circle cx=&quot;100&quot; cy=&quot;151&quot; r=&quot;98&quot;&#x2F;&gt;&lt;&#x2F;clipPath&gt;
&lt;&#x2F;defs&gt;
&lt;circle cx=&quot;100&quot; cy=&quot;151&quot; r=&quot;98&quot; fill=&quot;var(--fill-soft)&quot;&#x2F;&gt;
&lt;circle cx=&quot;100&quot; cy=&quot;249&quot; r=&quot;98&quot; fill=&quot;var(--fill-soft)&quot;&#x2F;&gt;
&lt;circle cx=&quot;100&quot; cy=&quot;249&quot; r=&quot;98&quot; clip-path=&quot;url(#vd-clip-top-v)&quot; fill=&quot;var(--fill-accent)&quot;&#x2F;&gt;
&lt;&#x2F;svg&gt;
&lt;div class=&quot;venn-labels&quot;&gt;
&lt;div class=&quot;venn-label venn-label-soft&quot;&gt;&lt;span&gt;aleatoric&lt;&#x2F;span&gt;&lt;span&gt;$\mathbb{E}[H[Y \mid x, \theta]]$&lt;&#x2F;span&gt;&lt;&#x2F;div&gt;
&lt;div class=&quot;venn-label venn-label-accent&quot;&gt;&lt;span&gt;epistemic&lt;&#x2F;span&gt;&lt;span&gt;$I[Y; \theta \mid x, D]$&lt;&#x2F;span&gt;&lt;&#x2F;div&gt;
&lt;div class=&quot;venn-label venn-label-soft&quot;&gt;$H[\theta \mid Y, x, D]$&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;venn-titles venn-titles-bottom&quot;&gt;
&lt;span&gt;$H[\theta \mid D]$&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;The left circle is the total uncertainty over outcomes $H[Y \mid x]$, the right circle is the posterior uncertainty over model parameters $H[\theta \mid D]$.&lt;&#x2F;p&gt;
&lt;p&gt;To minimize uncertainty in our model $\theta$, we must train a policy $\pi$ that maximizes this overlap, epistemic uncertainty, by seeking states where knowing $\theta$ would tell us the most about $Y$. The policy needs to go places where the model we have matters.&lt;&#x2F;p&gt;
&lt;p&gt;Concretely, if within our posterior $P(\theta \mid D)$, some models say $P(Y = a \mid x, \theta) &amp;gt; P(Y = b \mid x, \theta)$, and other models say the converse, we want to collect more instances of $x$ to determine whether $Y$ is $a$ or $b$. This marginal data helps us shrink parameter space $\theta$ as quickly as possible; this marginal data helps us learn as quickly as possible.&lt;&#x2F;p&gt;
&lt;p&gt;But what can be learned in the first place?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;learnable-vs-intractable&quot;&gt;Learnable vs Intractable&lt;&#x2F;h1&gt;
&lt;p&gt;You can’t fit a line to a parabola, sometimes even the best parameters are &lt;em&gt;wrong&lt;&#x2F;em&gt; if the model doesn’t make sense.&lt;&#x2F;p&gt;
&lt;p&gt;Some deterministic processes, like hash functions, are &lt;em&gt;designed to be&lt;&#x2F;em&gt; computationally intractable to run in inverse. Hash functions aren’t &lt;em&gt;random&lt;&#x2F;em&gt;, but they certainly don’t count as a process we can learn with more data.&lt;&#x2F;p&gt;
&lt;p&gt;What is randomness, anyway? Here is a completely deterministic simulation of 15 balls bouncing around in a 16×16 box, always starting from the same state. (Note that &lt;code&gt;n&lt;&#x2F;code&gt; is the number of particles in the &lt;strong&gt;right half&lt;&#x2F;strong&gt; of the box, and &lt;code&gt;H&lt;&#x2F;code&gt; is the entropy of the empirical distribution.)&lt;&#x2F;p&gt;
&lt;p&gt;Is $n$ random?&lt;&#x2F;p&gt;
&lt;!--
date: 2026-02-17
model: claude-opus-4-6
driver: Isaac Clayton
note: No prose on this website is generated, this is provenance for the embedded particle simulation code that follows.
--&gt;
&lt;div id=&quot;particle-widget&quot;&gt;
&lt;div id=&quot;particle-controls&quot;&gt;
&lt;span class=&quot;pill&quot;&gt;&lt;a class=&quot;tag&quot; id=&quot;particle-reset&quot; href=&quot;javascript:void(0)&quot;&gt;reset&lt;&#x2F;a&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;pill&quot;&gt;&lt;a class=&quot;tag&quot; id=&quot;particle-toggle&quot; href=&quot;javascript:void(0)&quot;&gt;pause&lt;&#x2F;a&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;pill&quot; id=&quot;particle-step-pill&quot; style=&quot;display:none&quot;&gt;&lt;a class=&quot;tag&quot; id=&quot;particle-step&quot; href=&quot;javascript:void(0)&quot;&gt;step&lt;&#x2F;a&gt;&lt;&#x2F;span&gt; &lt;code&gt;n = &lt;span id=&quot;particle-n&quot;&gt;0&lt;&#x2F;span&gt;&lt;&#x2F;code&gt; &lt;code&gt;H = &lt;span id=&quot;particle-h&quot;&gt;0.00&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;
&lt;&#x2F;div&gt;
&lt;div id=&quot;particle-sim&quot;&gt;
&lt;style&gt;
#particle-widget {
  display: flex;
  flex-direction: column;
  gap: 10pt;
}
#particle-sim {
  display: flex;
  align-items: stretch;
  gap: 10pt;
}
#particle-box {
  position: relative;
  aspect-ratio: 1;
  background: linear-gradient(to right, var(--fill-soft) 50%, var(--fill-bg) 50%);
  border: 2px solid var(--fill-soft);
  border-radius: 2px;
  overflow: hidden;
  flex: 2;
}
#particle-box .dot {
  position: absolute;
  width: 6.25%;
  height: 6.25%;
  transition: left 150ms linear, top 150ms linear;
}
#particle-box .dot::after {
  content: &#x27;&#x27;;
  display: block;
  width: 100%;
  height: 100%;
  background: var(--fill-accent);
  border-radius: 50%;
}
#particle-box .dot.bounce::after {
  background: var(--fill-text);
  transition: none;
}
#particle-hist {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
}
#particle-hist .hbar {
  display: flex;
  align-items: center;
  flex: 1;
  font-size: 0;
}
#particle-hist .hbar .fill {
  height: 100%;
  background: var(--fill-accent);
  border-radius: 2px;
  min-width: 0;
  transition: width 150ms linear;
}
#particle-hist .hbar.active .fill {
  background: var(--fill-text);
}
#particle-trace {
  width: 100%;
  height: 80px;
  background: var(--fill-bg);
  border: 2px solid var(--fill-soft);
  border-radius: 2px;
  display: block;
  box-sizing: border-box;
}
&lt;&#x2F;style&gt;
&lt;div id=&quot;particle-box&quot;&gt;&lt;&#x2F;div&gt;
&lt;div id=&quot;particle-hist&quot;&gt;&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;canvas id=&quot;particle-trace&quot;&gt;&lt;&#x2F;canvas&gt;
&lt;&#x2F;div&gt;
&lt;script&gt;
(() =&gt; {
  const N = 16;
  const NUM = 15;
  const g = 1.32471795724474602596;
  const a1 = 1.0 &#x2F; g;
  const a2 = 1.0 &#x2F; (g * g);
  const box = document.getElementById(&quot;particle-box&quot;);
  const particles = [];
  for (let i = 0; i &lt; NUM; i++) {
    const x = Math.floor(((0.5 + a1 * (i + 1)) % 1) * N);
    const y = Math.floor(((0.5 + a2 * (i + 1)) % 1) * N);
    const dx = (i % 2 === 0) ? 1 : -1;
    const dy = (Math.floor(i &#x2F; 2) % 2 === 0) ? 1 : -1;
    const el = document.createElement(&quot;div&quot;);
    el.className = &quot;dot&quot;;
    box.appendChild(el);
    particles.push({ x, y, dx, dy, el });
  }
  const pct = (v) =&gt; (v &#x2F; N * 100) + &quot;%&quot;;
  const nSpan = document.getElementById(&quot;particle-n&quot;);
  const hSpan = document.getElementById(&quot;particle-h&quot;);
  const hist = new Array(NUM + 1).fill(0);
  let ticks = 0;
  const histEl = document.getElementById(&quot;particle-hist&quot;);
  const hbars = [];
  for (let i = 0; i &lt;= NUM; i++) {
    const bar = document.createElement(&quot;div&quot;);
    bar.className = &quot;hbar&quot;;
    bar.innerHTML = &#x27;&lt;div class=&quot;fill&quot;&gt;&lt;&#x2F;div&gt;&#x27;;
    histEl.appendChild(bar);
    hbars.push(bar);
  }
  const traceCanvas = document.getElementById(&quot;particle-trace&quot;);
  const traceCtx = traceCanvas.getContext(&quot;2d&quot;);
  const TRACE_LEN = 200;
  const trace = [];
  const css = () =&gt; getComputedStyle(document.documentElement);
  const drawTrace = (count, push) =&gt; {
    const accentColor = css().getPropertyValue(&#x27;--fill-accent&#x27;).trim();
    const softColor = css().getPropertyValue(&#x27;--fill-soft&#x27;).trim();
    if (push !== false) {
      trace.push(count);
      if (trace.length &gt; TRACE_LEN) trace.shift();
    }
    const w = traceCanvas.width = traceCanvas.offsetWidth * devicePixelRatio;
    const h = traceCanvas.height = traceCanvas.offsetHeight * devicePixelRatio;
    traceCtx.clearRect(0, 0, w, h);
    &#x2F;&#x2F; midline
    const midY = h &#x2F; 2;
    traceCtx.strokeStyle = softColor;
    traceCtx.lineWidth = 2 * devicePixelRatio;
    traceCtx.setLineDash([6 * devicePixelRatio, 4 * devicePixelRatio]);
    traceCtx.beginPath();
    traceCtx.moveTo(0, midY);
    traceCtx.lineTo(w, midY);
    traceCtx.stroke();
    traceCtx.setLineDash([]);
    if (trace.length &lt; 2) return;
    &#x2F;&#x2F; fill under curve
    traceCtx.beginPath();
    traceCtx.moveTo(0, h);
    for (let i = 0; i &lt; trace.length; i++) {
      const x = (i &#x2F; (TRACE_LEN - 1)) * w;
      const y = h * (trace[i] &#x2F; NUM);
      traceCtx.lineTo(x, y);
    }
    traceCtx.lineTo(((trace.length - 1) &#x2F; (TRACE_LEN - 1)) * w, h);
    traceCtx.closePath();
    traceCtx.fillStyle = softColor;
    traceCtx.fill();
    &#x2F;&#x2F; stroke
    traceCtx.strokeStyle = accentColor;
    traceCtx.lineWidth = 2 * devicePixelRatio;
    traceCtx.lineJoin = &#x27;round&#x27;;
    traceCtx.lineCap = &#x27;round&#x27;;
    traceCtx.beginPath();
    for (let i = 0; i &lt; trace.length; i++) {
      const x = (i &#x2F; (TRACE_LEN - 1)) * w;
      const y = h * (trace[i] &#x2F; NUM);
      if (i === 0) traceCtx.moveTo(x, y);
      else traceCtx.lineTo(x, y);
    }
    traceCtx.stroke();
  };
  const render = () =&gt; {
    let count = 0;
    for (const p of particles) {
      p.el.style.left = pct(p.x);
      p.el.style.top = pct(p.y);
      if (p.x &gt;= N &#x2F; 2) count++;
    }
    nSpan.textContent = count;
    hist[count]++;
    ticks++;
    const max = Math.max(...hist);
    for (let i = 0; i &lt;= NUM; i++) {
      const w = max &gt; 0 ? (hist[i] &#x2F; max * 100) : 0;
      hbars[i].querySelector(&quot;.fill&quot;).style.width = w + &quot;%&quot;;
      hbars[i].className = i === count ? &quot;hbar active&quot; : &quot;hbar&quot;;
    }
    let H = 0;
    for (let i = 0; i &lt;= NUM; i++) {
      const p = hist[i] &#x2F; ticks;
      if (p &gt; 0) H -= p * Math.log2(p);
    }
    hSpan.textContent = H.toFixed(2);
    drawTrace(count);
  };
  const grid = new Uint8Array(N * N);
  for (const p of particles) grid[p.y * N + p.x] = 1;
  const oob = (x, y) =&gt; x &lt; 0 || x &gt;= N || y &lt; 0 || y &gt;= N;
  const filled = (x, y) =&gt; oob(x, y) || grid[y * N + x];
  const step = () =&gt; {
    for (const p of particles) {
      p.el.classList.remove(&quot;bounce&quot;);
      const odx = p.dx, ody = p.dy;
      const bx = filled(p.x + odx, p.y);
      const by = filled(p.x, p.y + ody);
      const bd = filled(p.x + odx, p.y + ody);
      if (bx || (!by &amp;&amp; bd)) p.dx = -odx;
      if (by || (!bx &amp;&amp; bd)) p.dy = -ody;
      if (odx !== p.dx || ody !== p.dy) {
        p.el.classList.add(&quot;bounce&quot;);
      }
    }
    for (const p of particles) {
      const nx = p.x + p.dx, ny = p.y + p.dy;
      if (!filled(nx, ny)) {
        grid[p.y * N + p.x] = 0;
        p.x = nx; p.y = ny;
        grid[ny * N + nx] = 1;
      }
    }
    render();
  };
  render();
  let iv = setInterval(step, 150);
  let running = true;
  const toggleBtn = document.getElementById(&quot;particle-toggle&quot;);
  const resetBtn = document.getElementById(&quot;particle-reset&quot;);
  toggleBtn.onclick = (e) =&gt; {
    e.preventDefault();
    if (running) { clearInterval(iv); running = false; toggleBtn.textContent = &quot;play&quot;; stepPill.style.display = &quot;&quot;; }
    else { iv = setInterval(step, 150); running = true; toggleBtn.textContent = &quot;pause&quot;; stepPill.style.display = &quot;none&quot;; }
  };
  const stepPill = document.getElementById(&quot;particle-step-pill&quot;);
  const stepBtn = document.getElementById(&quot;particle-step&quot;);
  stepBtn.onclick = (e) =&gt; {
    e.preventDefault();
    if (!running) step();
  };
  resetBtn.onclick = (e) =&gt; {
    e.preventDefault();
    if (running) { clearInterval(iv); running = false; toggleBtn.textContent = &quot;play&quot;; stepPill.style.display = &quot;&quot;; }
    grid.fill(0);
    for (let i = 0; i &lt; NUM; i++) {
      const p = particles[i];
      p.x = Math.floor(((0.5 + a1 * (i + 1)) % 1) * N);
      p.y = Math.floor(((0.5 + a2 * (i + 1)) % 1) * N);
      p.dx = (i % 2 === 0) ? 1 : -1;
      p.dy = (Math.floor(i &#x2F; 2) % 2 === 0) ? 1 : -1;
      p.el.classList.remove(&quot;bounce&quot;);
      grid[p.y * N + p.x] = 1;
    }
    hist.fill(0);
    trace.length = 0;
    ticks = 0;
    render();
  };
  window.matchMedia(&#x27;(prefers-color-scheme: dark)&#x27;).addEventListener(&#x27;change&#x27;, () =&gt; {
    if (!running &amp;&amp; trace.length &gt; 0) drawTrace(null, false);
  });
})();
&lt;&#x2F;script&gt;
&lt;p&gt;Well, no! Despite being chaotic, this simulation is not random! (Try resetting the simulation, and stepping it forward a couple of times to convince yourself of this fact.) For any given state $s$, we can deterministically step the system and observe $n$. There is no aleatoric uncertainty, no randomness. But this doesn’t feel fair.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s say we can only observe $n$, and want to predict how it evolves over time. Since this system is completely deterministic, if we had &lt;em&gt;perfect&lt;&#x2F;em&gt; information about the position and direction of each particle, we could build a model that perfectly captures $n$.&lt;&#x2F;p&gt;
&lt;p&gt;However, we can only &lt;em&gt;observe&lt;&#x2F;em&gt; $n$. We don’t know the &lt;em&gt;state&lt;&#x2F;em&gt; of the box. Even if we knew the dimensions of the box, the rules of the game, and the number of balls, it would be &lt;em&gt;very hard&lt;&#x2F;em&gt; to infer the position and direction of each particle purely from observing how $n$ changes over time.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;N.B.&lt;&#x2F;em&gt; Concretely, there are $\binom{256}{15}$ ways to place 15 particles on a 16×16 grid (~79 bits) and 4 directions each particle can take (30 bits). That’s 109 bits of state total. From $n$ we get $\leq 4$ bits of information per observation. It would take us $&amp;gt;28$ observations to gain $&amp;gt;109$ bits of information. We could only reconstruct the state at this point if we could use the information from each $n$ perfectly, and I would be surprised if there’s a computationally tractable way to do that.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The key insight is that although $n$ is not random, it &lt;em&gt;appears&lt;&#x2F;em&gt; to be random to an outside observer. Empirically, this is what statistics is: how do we model processes that we can not observe perfectly?&lt;&#x2F;p&gt;
&lt;p&gt;Even though the evolution of $n$ is deterministic, it is not &lt;em&gt;learnable&lt;&#x2F;em&gt;; it will look like a random variable sampled from some distribution. &lt;span class=aside&gt;The distribution is approximated with the histogram we draw to the side.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If a latent variable is not learnable, it might as well be random. So we might as well divide our equation for uncertainty further:&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{align}
\text{uncertainty} &amp;amp;= \text{aleatoric} + \text{epistemic} \\
&amp;amp;= \text{aleatoric} + (\text{learnable} + \text{intractable})
\end{align}$$&lt;&#x2F;p&gt;
&lt;p&gt;This split is a practical or computational one, but there are competing definitions. In general, a portion of epistemic uncertainty is intractable for one of two reasons:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;If our model class cannot represent the true dynamics, no amount of data will close the gap; you can’t fit a line to a parabola.&lt;&#x2F;li&gt;
&lt;li&gt;If observations never contain information present in some part of state space, that information is inaccessible through the information channel we have.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;In practice, we can train a policy $\pi$ to collect episodes that maximize epistemic uncertainty in a way that ignores this intractable component. If epistemic uncertainty $I[Y; \theta \mid x, D]$ is decreasing as $D$ grows, those bits are learnable. If it plateaus, they are intractable. Instead of maximizing epistemic uncertainty, we maximize &lt;em&gt;learning progress&lt;&#x2F;em&gt;: the rate at which epistemic uncertainty decreases as we collect more data.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;closing-thoughts&quot;&gt;Closing thoughts&lt;&#x2F;h1&gt;
&lt;p&gt;In theory, practice is theory. In practice, theory isn’t. Going from theory to practice requires some good engineering.&lt;&#x2F;p&gt;
&lt;p&gt;As you can imagine, measuring learning progress by taking the derivative of an estimated epistemic uncertainty value is… quite unstable. Modern research applies a whole slew of tricks to go from theory to practice. This engineering is where the excitement lies.&lt;&#x2F;p&gt;
&lt;p&gt;I care about this because I want to build data-efficient models of physical systems; the kind where episodes are expensive and you can’t afford to waste trajectories on noise. Flapping airplanes, for instance.&lt;&#x2F;p&gt;
&lt;p&gt;The next time I write about this, curiosity, I’ll talk about learning progress, stable exploration signals, RND’s practical success, how to compute epistemic and aleatoric uncertainty in practice (ensembles, BALD, SWAG, etc.), and other directions for sussing out learnable structure.&lt;&#x2F;p&gt;
&lt;p&gt;Until then, you might enjoy:&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;pdf&#x2F;2601.16175&quot;&gt;Learning to Discover at Test Time&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;!-- modified from https:&#x2F;&#x2F;zola.discourse.group&#x2F;t&#x2F;maths-support-via-mathjax&#x2F;1000 --&gt;
&lt;script&gt;
window.MathJax = {
  tex: {
    inlineMath: [[&#x27;$&#x27;,&#x27;$&#x27;]],
    displayMath: [[&#x27;$$&#x27;,&#x27;$$&#x27;]],
  },
};
&lt;&#x2F;script&gt;
&lt;script id=&quot;MathJax-script&quot; defer src=&quot;&#x2F;mathjax&#x2F;tex-mml-chtml.js&quot;&gt;&lt;&#x2F;script&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Curiosity, some intuition</title>
        <published>2026-02-13T00:00:00+00:00</published>
        <updated>2026-02-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-13/"/>
        <id>https://slightknack.dev/daily/2026-02-13/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-13/">&lt;p&gt;I figure I’ll publish this long post in a couple parts, over today and tomorrow or perhaps the day after (as tomorrow is going to be a busy day). In today’s post I plan to go over curiosity and the Noisy TV Problem, to help build some practical and mathematical intuition. Tomorrow, I’d like to talk about some information theory, specifically aleatoric and epistemic uncertainty, and how this framing can be used to improve learning through exploration.&lt;&#x2F;p&gt;
&lt;p&gt;One of my favorite ML papers of all time was published in 2018. It’s called &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1810.12894&quot;&gt;Exploration by Random Network Distillation (RND)&lt;&#x2F;a&gt;, by Burda et al.. &lt;span class=aside&gt;Some other favorites are &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mlg.eng.cam.ac.uk&#x2F;pub&#x2F;pdf&#x2F;DeiRas11.pdf&quot;&gt;PILCO&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;pdf&#x2F;2210.08277&quot;&gt;DLGN&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt; Let’s start with some background:&lt;&#x2F;p&gt;
&lt;h1 id=&quot;rl-and-rewards&quot;&gt;RL and rewards&lt;&#x2F;h1&gt;
&lt;p&gt;Reinforcement Learning (RL) is concerned with learning a &lt;em&gt;policy&lt;&#x2F;em&gt; (rules about how to act in a situation) to complete some general objective in an &lt;em&gt;environment&lt;&#x2F;em&gt;. The environment can be anything, like chess or a video game or chatting with a human or writing code or working on a math problem. The objective is traditionally given using a &lt;em&gt;reward function&lt;&#x2F;em&gt;, which assigns a numeric value to each possible state of an environment. &lt;span class=aside&gt;For example, +1 if the policy did something good, -1 if it did not.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To train an RL policy, we traditionally “roll out” many &lt;em&gt;episodes&lt;&#x2F;em&gt;—or recordings—of a policy acting in a given environment. We then calculate the &lt;em&gt;total reward&lt;&#x2F;em&gt; for each episode by applying the reward function to each state in the episode and summing. Policies are then iteratively trained to maximize the total reward collected over the course of an episode, using techniques like &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1707.06347&quot;&gt;PPO&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2402.03300&quot;&gt;GRPO&lt;&#x2F;a&gt;, etc..&lt;&#x2F;p&gt;
&lt;p&gt;As you can imagine, it’s hard to come up with a good reward function. If a new policy is learning chess, initialized to make random moves, it is very unlikely that the policy will randomly checkmate its opponent. A reward function that assigns +100 to winning, -100 to losing, and 0 reward otherwise is considered to be a very &lt;em&gt;sparse&lt;&#x2F;em&gt; reward signal. Sparse rewards are few and far between, so it’s hard for the policy to learn stepping-stone milestones and make partial progress. To overcome this problem, we can enrich reward functions with added heuristics—e.g. which side has more pieces, whether a piece was taken on the last turn, whether a king was put in check, etc.—and these heuristics can provide enough of an intermediate &lt;em&gt;dense&lt;&#x2F;em&gt; reward signal to guide the policy towards discovering the large rewards we know exist at the end.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;human-curiosity&quot;&gt;Human curiosity&lt;&#x2F;h1&gt;
&lt;p&gt;Why do we know things exist? Side-stepping existentialism, we know things exist because people are naturally curious. When we don’t know what the reward for doing something is, we generally try it out. We like to learn and make discoveries; this pioneering spirit has pushed us to the four corners of the globe. You could say that humans are intrinsically motivated to explore; we find boredom to be boring, and boring in excess quantities to be bad. We feel a thrill of surprise when we experience something new and unexpected.&lt;&#x2F;p&gt;
&lt;p&gt;There is this idea of the “expected free energy” model of intelligence. (This is more “nice model to think about” than an “empirical claim about how the brain works”.) It goes something like this: if you accept that intelligence is prediction, then it follows that an intelligent system, like the brain, is a prediction-error-minimization machine. To improve its models of the world, the brain seeks out states that, when observed, will decrease prediction error.&lt;&#x2F;p&gt;
&lt;p&gt;Talking about humans, you can take the expected free energy model pretty far: there might be some predictions wired into us, like “feeling full”. Then, hunger is the feeling of the prediction error we intuit when our body is not full. Likewise, when you pick up an orange, your brain predicts that you will be holding an orange, and your body, being acutely aware of the fact that you are not holding an orange, moves to correct that error. There’s this wave of prediction crashing into this wave of sensory input, and the differences are noted in one direction to learn and in the other direction to act.&lt;&#x2F;p&gt;
&lt;p&gt;Returning to the land of RL: there’s a current school of thought that instead of hand-crafting bespoke &lt;em&gt;dense&lt;&#x2F;em&gt; reward functions, we should emulate expected free energy when training policies. We decompose the reward function into two parts: the sparse reward we ultimately care about (e.g. win&#x2F;loss), and the dense reward tracking the “exploration” of new states. We can combine these two parts together to derive a reward function that better guides the policy towards interesting and rewarding states.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;curiosity-and-rnd&quot;&gt;Curiosity and RND&lt;&#x2F;h1&gt;
&lt;p&gt;How can you quantify which states are new and exciting to explore, and which states are routine and boring?&lt;&#x2F;p&gt;
&lt;p&gt;Usually we treat an environment like something of a black box. The environment has an internal state, which we’ll denote with $s$. This state $s$ is then mapped to some observation $o$ (which may not contain all the information present in $s$). Our policy $\pi$ receives a history of observations $o^*$, and from it we sample an action $a$. This action is then sent to the environment, and the next state $s’$ is stochastically determined from $(s, a)$, the state and the action. In the literature, this is called a &lt;em&gt;partially-observable markov decision process&lt;&#x2F;em&gt;, or POMDP for short (let’s not worry about reward for now).&lt;&#x2F;p&gt;
&lt;p&gt;As a simplifying assumption, let’s say observations map fairly directly to states. Suppose we had an embedding of all observations in a well-structured vector space $O$. Then, for any pair of observations $a, b \in O$, we can directly measure their distance using $|a - b|^2$.&lt;&#x2F;p&gt;
&lt;p&gt;Imagine we collected episodes using a policy $\pi$, and recorded all observation vectors from each episode in a large database $D \subseteq O$. Upon receiving a new observation $o’$, one way to determine how “novel” the observation was would be to see how far it is from each of the previously seen observation vectors in the database, and return the smallest distance:&lt;&#x2F;p&gt;
&lt;p&gt;$$\text{novelty}(o’) = \min_{d \in D} |o’ - d|^2$$&lt;&#x2F;p&gt;
&lt;p&gt;This is a sound approach in theory, but in practice, if $D$ grows to be quite large, this process could get quite slow: calculating novelty is linear in $|D|$.&lt;&#x2F;p&gt;
&lt;p&gt;The Random Network Distillation paper proposes a clever hack. The key idea is to approximate $D$, the set of seen observations, using two neural networks. In more detail:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;We start with two neural networks mapping from observation space $O$ to a smaller &lt;em&gt;embedding&lt;&#x2F;em&gt; space $V$: call the first the &lt;em&gt;target&lt;&#x2F;em&gt; network $T$ and the second the &lt;em&gt;predictor&lt;&#x2F;em&gt; network $\hat{T}$. Initialize $T$ and $\hat{T}$ using different initial random weights, but freeze the weights of the target.&lt;&#x2F;li&gt;
&lt;li&gt;In each training step, using the current policy $\pi$, collect an episode of observations $o_{1:n}$. For each observation $o_i \in o_{1:n}$, use the target network $T$ to embed this observation: $v_{i} = T(o_{i})$. Then use the predictor $\hat{T}$ to estimate ${\hat{v}}_i = \hat{T}(o_i)$, the target’s embeddings.&lt;&#x2F;li&gt;
&lt;li&gt;We can calculate the “surprise” for any individual observation $o_i$ to be $\text{surprise}(o_i) = |T(o_i) - \hat{T}(o_i)|^2 = |v_i - \hat{v}_i|^2$, the MSE between the embeddings. If the target and the predictor agree, surprise is low. Otherwise, surprise is high.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;This is the important part:&lt;&#x2F;strong&gt; As we train, keep the target $T$ fixed, but train the predictor $\hat{T}$ to predict the output of $T$ for the previous observation and embedding pairs $(o, v)^*$ we have seen. This means that, for observations similar to previously-seen observations, the surprise $|v - \hat{v}|^2$ will be very small, but for new observations, the surprise will be very large.&lt;&#x2F;li&gt;
&lt;li&gt;Label each observation with surprise in lieu of (or in combination with) a reward function, to intrinsically motivate the behavior of “seeking out surprising states” in the policy. (Otherwise train the policy $\pi$ along with the predictor $\hat{T}$ as you normally would.)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This intrinsic exploration reward is often called &lt;em&gt;curiosity&lt;&#x2F;em&gt;, and I like that name quite a bit. (What was revolutionary about RND was that it made great progress on &lt;em&gt;Montezuma’s Revenge&lt;&#x2F;em&gt;, an old Atari game that is very exploration-driven and thus provides very sparse rewards. It’s hard to emphasize how plain &lt;em&gt;cool&lt;&#x2F;em&gt; this result was at the time.)&lt;&#x2F;p&gt;
&lt;h1 id=&quot;rnd-and-tv&quot;&gt;RND and TV&lt;&#x2F;h1&gt;
&lt;p&gt;Let’s say you’re a very curious fellow. I show you a coin. Will it land heads or tails?&lt;&#x2F;p&gt;
&lt;p&gt;I flip the coin. Tails! Were you right? I flip it again and again. For whatever reason, you can only seem to predict when it lands heads-up about half the time. Stochastic? Sounds pretty unfair. I can hardly pronounce the word. How are you ever supposed to learn my trick!?&lt;&#x2F;p&gt;
&lt;p&gt;Different from RND, previous exploration approaches used forward-prediction to estimate surprise. &lt;span class=aside&gt;Surprise from prediction is very intuitive: “how different is what I expected from what I observed?”&lt;&#x2F;span&gt; Forward-prediction, however, falls apart if &lt;em&gt;state transitions&lt;&#x2F;em&gt; are random: it’s impossible to predict a coin flip, for instance. A TV playing static is a coin-flip per pixel. Agents getting stuck trying to predict noise is called, quite unsurprisingly, the &lt;em&gt;Noisy TV Problem&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;RND, by virtue of &lt;em&gt;not&lt;&#x2F;em&gt; using forward prediction, is not vulnerable to stochastic state transitions like earlier approaches were, and this is one of the key contributions of the paper. There are a couple issues with RND as I’ve formulated it in this post, though:&lt;&#x2F;p&gt;
&lt;p&gt;First, while RND &lt;em&gt;overcame&lt;&#x2F;em&gt; a temporal version of the Noisy TV Problem, policies can get distracted. Imagine an environment with a TV that changed to a random, unpredictable picture every frame. The target $T$ would always produce some unique random embedding $v_{\text{wow}}$, and our predictor $\hat{T}$ would produce a completely different embedding $\hat{v}_{\text{hmm}}$. As these embeddings are different (because the picture is new!), the policy receives a very high reward for staying near and observing the TV. The policy becomes glued to the TV, addicted to a random stream of meaningless information that is impossible to learn.&lt;&#x2F;p&gt;
&lt;p&gt;Second, as a brief aside, we assumed that states could be mostly recovered from observations. This means we assumed (mostly-)total information, which is true of games like chess, but not true in general. If you’re playing a video game, there are a lot of things the computer is keeping track of in $s$ (like the location of enemies offscreen), that cannot necessarily be deduced from observation alone. In the real world, we’d like some way to model the underlying environment &lt;em&gt;state&lt;&#x2F;em&gt;, and not just our observations of this unobservable state.&lt;&#x2F;p&gt;
&lt;p&gt;Taking a step back, though: &lt;em&gt;why&lt;&#x2F;em&gt; does RND work at all?&lt;&#x2F;p&gt;
&lt;p&gt;Is it possible to solve the Noisy TV problem generally, in a way that is theoretically sound and computationally tractable? The Noisy TV Problem goes far beyond literal noise in the environment: anything that is random, or otherwise too complicated for the model to realistically learn, may become an unwanted distraction. &lt;span class=aside&gt;You can’t fit a line to a parabola, no matter how much data you have.&lt;&#x2F;span&gt; How do we seek out the signal in the noise?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;you-ll-have-to-stay-tuned-because-it-s-way-too-late-and-i-m-going-to-bed&quot;&gt;You’ll have to stay tuned because it’s way too late and I’m going to bed&lt;&#x2F;h1&gt;
&lt;p&gt;Surprise! I’m sure you saw that coming. I’m *so good* at writing exposition I didn’t even get to the fun part!&lt;&#x2F;p&gt;
&lt;p&gt;Tomorrow I’ll write a little primer on information theory, including entropy, surprisal, aleatoric uncertainty, epistemic uncertainty, total uncertainty (spoiler: it’s just entropy!), and learnable uncertainty. The key point is that we want &lt;em&gt;curiosity&lt;&#x2F;em&gt; to map to the “number of learnable bits collected throughout the course of an episode”.&lt;&#x2F;p&gt;
&lt;p&gt;If I have time tomorrow, and if not sometime the day after or next week, I will write about how to compute and approximate these quantities, some state of the art approaches and other tricks (ensembles, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1902.02476&quot;&gt;SWAG&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1112.5745&quot;&gt;BALD&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1503.05671&quot;&gt;KFAC&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2106.09675&quot;&gt;BAIT&lt;&#x2F;a&gt;, etc.). I’ll then wrap things up by talking about some approaches I find fun and&#x2F;or promising, like &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2106.01345&quot;&gt;Decision Transformers&lt;&#x2F;a&gt; with “learnable bits of information to go” in place of reward-to-go. &lt;span class=aside&gt;Let’s make &lt;em&gt;mid-training&lt;&#x2F;em&gt; a thing!&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;!--- RND
  - curiosity
  - state, action, reward, done
- Noisy TV Problem
- Information theory
  - Entropy
  - KL Divergence
    - connects surprisal to decomposition
  - Surprisal
- Entropy is total uncertainty
  - Aleatoric + Epistemic
  - Aleatoric
    - Noise in environment
  - Epistemic
    - Noise in model
- We want to reduce epistemic uncertainty
  - Collect trajectories that maximize epistemic bits
- What is learnable anyway?
  - RAIR
  - Epistemic bits -&gt; learnable bits
    - learning progress, rate of epistemic bit decrease
      - hard to compute, noisy
  - you can&#x27;t fit a line to a curve, some things can&#x27;t be learned, you should not treat this error as epistemic uncertainty.
- How to compute things
  - Ensembles and decomposition
    - BALD
    - SWAG, distribution over weights
  - Fisher information and KFAC
    - BAIT
- Pre-training, post-training. Mid-training?
  - Expected free energy; goal--&gt;
&lt;p&gt;To tide you over until next time, you might enjoy this thematically-appropriate post:&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jessylin.com&#x2F;2025&#x2F;10&#x2F;20&#x2F;continual-learning&quot;&gt;The Continual Learning Problem&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;!-- modified from https:&#x2F;&#x2F;zola.discourse.group&#x2F;t&#x2F;maths-support-via-mathjax&#x2F;1000 --&gt;
&lt;script&gt;
window.MathJax = {
  tex: {
    inlineMath: [[&#x27;$&#x27;,&#x27;$&#x27;]],
    displayMath: [[&#x27;$$&#x27;,&#x27;$$&#x27;]],
  },
};
&lt;&#x2F;script&gt;
&lt;script id=&quot;MathJax-script&quot; defer src=&quot;&#x2F;mathjax&#x2F;tex-mml-chtml.js&quot;&gt;&lt;&#x2F;script&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Real Numbers?</title>
        <published>2026-02-12T00:00:00+00:00</published>
        <updated>2026-02-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-12/"/>
        <id>https://slightknack.dev/daily/2026-02-12/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-12/">&lt;p&gt;I had two assignments due today, and working on those consumed most of my time, so unfortunately today’s post will be rather short.&lt;&#x2F;p&gt;
&lt;p&gt;One iconic quote from a classmate in my mathematics&#x2F;philosophy class today (&lt;code&gt;24.118&lt;&#x2F;code&gt;):&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“So… mathematics is the art of making stuff up, and then justifying it with made up notation?”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;(TIL: There’s a &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;plato.stanford.edu&#x2F;entries&#x2F;philosophy-mathematics&#x2F;&quot;&gt;whole branch of philosophy&lt;&#x2F;a&gt; dedicated to determine whether numbers and symbols exist, and the extent to which you can derive meaning from manipulating them.)&lt;&#x2F;p&gt;
&lt;p&gt;I also read a lot today, specifically about the history of legislation surrounding &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.law.cornell.edu&#x2F;uscode&#x2F;text&#x2F;47&#x2F;223&quot;&gt;47 U.S.C. §223&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.law.cornell.edu&#x2F;uscode&#x2F;text&#x2F;47&#x2F;230&quot;&gt;§230&lt;&#x2F;a&gt;. I also ran into this &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.eff.org&#x2F;effector&#x2F;8&#x2F;14&quot;&gt;EFF Effector from 1995&lt;&#x2F;a&gt;, which provides some context&#x2F;commentary, along with this funny &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;time.com&#x2F;archive&#x2F;6727584&#x2F;my-flag-your-shorts&#x2F;&quot;&gt;essay about flags&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“But our culture is in truly bad shape if we have come to define respecting something as the failure to set it on fire.”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Well, tomorrow I should have some more time. In lieu of a full post today, here’s a fun math-related post. Are types a property of terms, or just annotations?&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ericnormand.me&#x2F;article&#x2F;church-vs-curry-types&quot;&gt;Eric Normand, Church vs Curry Types&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Precommitment</title>
        <published>2026-02-11T00:00:00+00:00</published>
        <updated>2026-02-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-11/"/>
        <id>https://slightknack.dev/daily/2026-02-11/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-11/">&lt;div class=epigraph&gt;
&lt;p&gt;“Take me and bind me to the crosspiece half way up the mast; bind me as I stand upright, with a bond so fast that I cannot possibly break away, and lash the rope’s ends to the mast itself. If I beg and pray you to set me free, then bind me more tightly still.”&lt;&#x2F;p&gt;
&lt;p&gt;“I made by frowning to my men that they should set me free; but they quickened their stroke, and Eurylochus and Perimedes bound me with still stronger bonds.”&lt;&#x2F;p&gt;
&lt;p&gt;&lt;cite&gt;— Homer, &lt;em&gt;The Odyssey&lt;&#x2F;em&gt;, Book XII (translated, Samuel Butler)&lt;&#x2F;cite&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;If I gave you a pill that would change you and make &lt;em&gt;playing the viola&lt;&#x2F;em&gt; your goal, passion, and reason-for-being in life, would you take it? &lt;span class=aside&gt;Viola players: substitute viola for &lt;em&gt;tuba&lt;&#x2F;em&gt;.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Obviously not! Playing the viola is nice and all, but the desire I proposed is not &lt;em&gt;yours&lt;&#x2F;em&gt;. Your desires aren’t fixed, but they are &lt;em&gt;yours&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A while back, a mentor gave me this advice:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Isaac, you’re pretty good at math. Math is static. You solve a problem one day, and the solution will be the same the next day. People are different, people change. You think you understand what makes them tick one day, and the next day they surprise you. You have to pay attention.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;People grow and change. I grow and change. The dream I had of being a pilot as a kid is still buried in there somewhere, but it’s not fully mine. It’s the dream of an earlier self.&lt;&#x2F;p&gt;
&lt;p&gt;We are playing a constant game of cooperation with our future selves. (“I’ll do this work now so you don’t have to stress about it tomorrow.”) Sometimes we drop the ball. (“I wish I did this yesterday! What was I thinking!?”)&lt;&#x2F;p&gt;
&lt;p&gt;Cooperation built on iterated self-interest is fragile. Procrastination is placing your current desires above those of &lt;a href=&quot;&#x2F;daily&#x2F;2026-02-10&#x2F;&quot;&gt;your future self&lt;&#x2F;a&gt;, in that sense.&lt;&#x2F;p&gt;
&lt;p&gt;How do you make cooperation robust to future temptation? The answer lies in &lt;em&gt;precommitment&lt;&#x2F;em&gt;, binding yourself now so future-you can’t defect. You have the capacity to do anything, but there are &lt;a href=&quot;&#x2F;daily&#x2F;2026-02-05&#x2F;&quot;&gt;many things you choose not to do&lt;&#x2F;a&gt;. Promising to never do something, or to always do something, is precommitment.&lt;&#x2F;p&gt;
&lt;p&gt;Consider Ulysses and the mast. &lt;span class=aside&gt;From the epigraph at the beginning.&lt;&#x2F;span&gt; He desired to hear the siren’s song, but he precommitted not to succumb to it. In binding himself he constrained his action space, and made it through.&lt;&#x2F;p&gt;
&lt;p&gt;This morning I had an erg piece. I wasn’t feeling the best this morning, but I wasn’t feeling bad enough not to do it.&lt;&#x2F;p&gt;
&lt;p&gt;Our cox Gav says, “once you start a piece, never get off.” It’s better to struggle through to the end than to even tempt building the habit of getting off halfway through.&lt;&#x2F;p&gt;
&lt;p&gt;Not getting off is a precommitment in the body. It is deciding before the piece starts that quitting is not in the action space. It’s removing the Exit. Creating a &lt;a href=&quot;&#x2F;daily&#x2F;2026-01-11&#x2F;&quot;&gt;crucible where you can be refined&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Likewise, this &lt;a href=&quot;&#x2F;daily&#x2F;2026-02-02&#x2F;&quot;&gt;habit of daily writing&lt;&#x2F;a&gt; I’ve been working to establish is an extension of the same principle. I have &lt;a href=&quot;&#x2F;blog&#x2F;writing&#x2F;&quot;&gt;wanted to write daily&lt;&#x2F;a&gt; for a long time — and now I do. I’ve precommitted to never publish anything on this website under my name that I did not author myself, and I’ve remained true to that commitment. In the process, I’ve &lt;a href=&quot;&#x2F;daily&#x2F;2026-02-04&#x2F;&quot;&gt;improved a lot as a writer&lt;&#x2F;a&gt;, both in terms of the quality of my prose and my consistency. But I’ve missed days, and published pieces that could have been better, in retrospect.&lt;&#x2F;p&gt;
&lt;p&gt;Precommitment does not require perfection. Perfection is a process, motion towards a desired state, and not a state in and of itself. Precommitments can be temporary or eternal. You can precommit to not get off the next piece, or not get off any piece.&lt;&#x2F;p&gt;
&lt;p&gt;If you break a rule, the game stops, and you stop playing. Yesterday I included a story about a couple that feared they were going to break up, and fed that anxiety until it happened. The biggest issue here was that breakup was seen as final; they denied themselves the possibility of re-entering.&lt;&#x2F;p&gt;
&lt;p&gt;That’s the choice with precommitment. Do we re-enter or withdraw? Re-entering is harder, but precommitting to re-enter is the strongest type of promise there is. Precommitting to re-enter, even as the game evolves and the rules change, is the only way to cooperate beyond reason to preserve collective agency.&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;zanlib.dev&#x2F;blog&#x2F;reliable-signals-of-honest-intent&quot;&gt;Zanlib: Reliable Signals of Honest Intent&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Take another turn</title>
        <published>2026-02-10T00:00:00+00:00</published>
        <updated>2026-02-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-10/"/>
        <id>https://slightknack.dev/daily/2026-02-10/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-10/">&lt;p&gt;The other day I wrote a post titled &lt;a href=&quot;&#x2F;daily&#x2F;2026-02-04&#x2F;&quot;&gt;&lt;em&gt;On craft and AI&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; that analogized AI to a hammer, a tool. I like the tool analogy because it does not hide accountability; it does not create an &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;aworkinglibrary.com&#x2F;writing&#x2F;accountability-sinks&quot;&gt;“accountability sink”&lt;&#x2F;a&gt;. The actions of a tool are the responsibility of its handler.&lt;&#x2F;p&gt;
&lt;p&gt;There’s a simple game called the &lt;em&gt;Prisoner’s Dilemma&lt;&#x2F;em&gt; where two people square off. Each player can choose to “cooperate” or to “defect”. If both players cooperate, each wins a small amount. If both players defect, both win nothing. But! If one player cooperates and the other player defects, the player who defects takes everything, and the other loses:&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;th&gt;Cooperate&lt;&#x2F;th&gt;&lt;th&gt;Defect&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;Cooperate&lt;&#x2F;th&gt;&lt;td&gt;+3, +3&lt;&#x2F;td&gt;&lt;td&gt;-1, +5&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;th&gt;Defect&lt;&#x2F;th&gt;&lt;td&gt;+5, -1&lt;&#x2F;td&gt;&lt;td&gt;0, 0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;I’m sure you can see how this game goes. Both players start off well-intentioned and choose to cooperate. One player realizes she can win a much larger prize if she defects. The other player is suspicious that his opponent might defect and—to not play the fool—decides he must defect as well. “Losing a little is a lot better than losing everything.” If you play just one round of the game, defect-defect is the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Nash_equilibrium&quot;&gt;&lt;em&gt;Nash equilibrium&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;. It is the rational move for both players to defect.&lt;&#x2F;p&gt;
&lt;p&gt;Where this game gets interesting is when you play multiple rounds against the same opponent. You might both start out cooperating because there’s a long game ahead. If, at some point, your opponent decides to defect, while you decide cooperate, you can punish them in the next round by defecting in turn. The natural result of “punishing adversarial behavior” incentivizes cooperative play. If you don’t know when the game is going to end, the rational move becomes “always cooperate”.&lt;&#x2F;p&gt;
&lt;p&gt;This version of the game is called the &lt;em&gt;Iterated Prisoners Dilemma&lt;&#x2F;em&gt;. It encourages cooperation. In real life, we find ourselves playing many games that share its structure. These games are not zero sum, are played over long time horizons, and encourage cooperation.&lt;&#x2F;p&gt;
&lt;p&gt;Now suppose you and I are playing a game of the Iterated Prisoner’s Dilemma long into the night. Having been tipped off by the fates, you know that the game will end on the next turn. I, however, do not: I will choose “cooperate” on the next turn. You face a rational temptation: you can “defect” and get away with it.&lt;&#x2F;p&gt;
&lt;p&gt;Throughout all of human history, we have played many iterated games that form the basis of civilization. One narrative is that, on the whole, societies that have chosen to play “cooperate” consequently improve their lot: they have won, and will continue to win. Cooperation is not coercion; it cannot be enforced in a top-down manner. Instead, a cooperative society is the emergent consequence of the individual choices of many citizens, kept alive through education and culture.&lt;&#x2F;p&gt;
&lt;p&gt;Technologists often talk about the singularity. This is the point past which all games we have been playing… stop, or at least, start changing faster than we can understand and play them.&lt;&#x2F;p&gt;
&lt;p&gt;If you, as an individual, &lt;em&gt;truly&lt;&#x2F;em&gt; believe that there will be a singularity, a technological phase transition, be it in &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ai-2027.com&#x2F;&quot;&gt;2027&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;The_Singularity_Is_Near&quot;&gt;2029&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;campedersen.com&#x2F;singularity&quot;&gt;2034&lt;&#x2F;a&gt;, what does that tell me about your perspective towards cooperation in the coming years?&lt;&#x2F;p&gt;
&lt;p&gt;Throughout all of human history there have always been people that have said, “well, the world is going to crap. I might as well make my bag, and stock up on some supplies, at least while I still can.” And that’s a perfectly rational way to live! You live this way, and tides recede, the ships run aground. A society whose individuals believe this will die.&lt;&#x2F;p&gt;
&lt;p&gt;How wonderful is it to stand at this place, in this time, on the doorstep of the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;borretti.me&#x2F;fiction&#x2F;maker-of-rivers&quot;&gt;noocene&lt;&#x2F;a&gt;. The game will only stop if we choose for it to stop, and believing it will stop is that choice. Once we believe, shared reality fragments into competing individual claims, and cooperative structures built on shared trust collapse. In that world it becomes my word against yours, &lt;a href=&quot;&#x2F;daily&#x2F;2026-02-05&#x2F;&quot;&gt;material against reality&lt;&#x2F;a&gt;, and everything unwinds.&lt;&#x2F;p&gt;
&lt;p&gt;I admit, not all games we play now are great games to play. Cooperation scales beautifully alongside its externalities.&lt;&#x2F;p&gt;
&lt;p&gt;There are many better games I wish we could play instead, those whose payoffs don’t depend on someone else’s loss.&lt;&#x2F;p&gt;
&lt;p&gt;Do we decide to invoke a Landian “Exit”, and withdraw entirely, hoping that our rate of asymptotic growth exceeds that of those around us? Or do we decide to rewrite the rules of the games we play, in tune with the world around us, and maintain that any action must be a means towards some good end? To Exit is to defect.&lt;&#x2F;p&gt;
&lt;p&gt;When Hooke discovered his law, it was determined that everything must be made of springs, to a first or second order. With the industrial revolution, it became clear that the body must be a machine. With the telegraph, the nerves a switchboard. With the computer, the mind a processor. Technology has always been a lens through which we see the world and a mirror through which we perceive ourselves. Each metaphor recasts our ideal of what is “rational”, and therefore what “cooperate” and “defect” mean. AI paints a &lt;code&gt;--s 750 --chaos 30 --ar 1:1&lt;&#x2F;code&gt; portrait of this generation. It is the first technology that we treat as an actor to which we can hand responsibility, and so we do.&lt;&#x2F;p&gt;
&lt;p&gt;When people defect, they stop acting as though collective action is possible. There is this narrative around AI, that it will save us from “the coming catastrophes. It will handle peace, it will handle prosperity.” It will rewind time to, well, whenever it was before “everything started going wrong”. AI offers the absolution of personal responsibility, on demand. A willing recipient of every pointed finger. Ever apologetic, admittedly wrong. “You’re absolutely right!” (but who is the model trying to impress?) To the average person, AI is no longer about multiplying matrices to solve Atari. The means has become the end, and “the end is near.”&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;I think it’s silly to believe in all this. A closed game, like the prisoner’s dilemma, has symmetric information, is well defined, with known payoff. Real life is messy; none of this holds for civilization.&lt;&#x2F;p&gt;
&lt;p&gt;While the model is a toy, the temptation to defect is not. Engaging in argument, there are two singularities. The first is that the game ends: in this case, it is rational to defect. But &lt;em&gt;if&lt;&#x2F;em&gt; the singularity is not a known final round, and rather a belief that the game will change, then perhaps it &lt;em&gt;is&lt;&#x2F;em&gt; rational to cooperate more, as the stakes are higher. Regardless, the temptation to defect does not depend on whether the singularity materializes as imagined; justifying defection only requires enough people to believe that it will.&lt;&#x2F;p&gt;
&lt;p&gt;In competition between societies, the society with the best asymptotics, or potential for growth, will win out in the long run. This general principle says nothing about the internal structure of the society.&lt;&#x2F;p&gt;
&lt;p&gt;Across all systems, and at all scales, we see cooperation &lt;em&gt;internally&lt;&#x2F;em&gt; and competition &lt;em&gt;externally&lt;&#x2F;em&gt;. Company in competition against company, as teams within companies cooperate. The gears in a car as it races another. Proteins within cells working together as cells race to divide. Internal cooperation is the structure that allows a system to externally compete.&lt;&#x2F;p&gt;
&lt;p&gt;When a system has poor internal structure, it has become too large. The components within it cease to cooperate and then wrestle for control. Competition can be a refining furnace through which something better is forged. Competition can also be the loose spring that shakes the system apart.&lt;&#x2F;p&gt;
&lt;p&gt;If you believe, according to a “last-turn singularity”, that the game will stop, regardless of what you choose, then it makes sense to compete, to let things unravel, to build something new. What I’m making is a prescriptive claim about my values. I’d like to exist embedded in a profoundly cooperative system that preserves my ability to act. If you believe in a “game-changing singularity”, there is no dilemma here. It’s a relay race, not a sprint. Don’t drop the baton.&lt;&#x2F;p&gt;
&lt;p&gt;Accelerationism claims that capital and technology combine to form a runaway process that doesn’t care about my individual cooperation. In a realist sense, this is true. At a societal level, we know cooperation wins. As an individual, however, where do I stand in relationship to this process? Do I shape it and guide it? Do I ignore it? Do I pour my time and talents into it? Do I let it consume my attention?&lt;&#x2F;p&gt;
&lt;p&gt;Or do I work to become “deeply okay” with all that is outside of my control, and focus on making the best decisions I can for questions within my control?&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;I’m worried that my friends are treating the singularity as an accountability sink for defection. When people choose to defect, they pick a smaller action space. They say, “Everything is now outside of my control. I must seize what little is left inside of my control; I must do what I can to preserve myself.” We anticipate the conclusion, we jump the gun.&lt;&#x2F;p&gt;
&lt;p&gt;Time flies like an arrow, and arrows follow their natural course. If an arrow is about to miss its target, applying a force in the direction of the target will serve no purpose but to make the arrow miss sooner. A force orthogonal to the vector of motion closes it the quickest. These orthogonal contributions are what’s worth preserving; it’s how we can find meaningful work in the face of accelerating change.&lt;&#x2F;p&gt;
&lt;p&gt;Back to the game. There’s a story that goes something like this:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Two lovers met in a city. One was leaving the following year. Worried about it, they decided to talk. In conversation, they determined that long distance was hard, and if it was truly in the cards, they would find one another again. It would make sense to break up when the other left, and enjoy the time they had left together.&lt;&#x2F;p&gt;
&lt;p&gt;A week later, one said to the other, “I’m worried about breaking up at the end of the year. We should break up in half a year, to give each other time and space to move on before the move.”&lt;&#x2F;p&gt;
&lt;p&gt;The other replied, “I was thinking the same thing. I love you, but if we’ll be breaking up in six months, we’ll be worried about it the whole time, and then we won’t really be able to enjoy our time together. Let’s break up a month from now, and make these next 30 days the best days we’ve ever had.”&lt;&#x2F;p&gt;
&lt;p&gt;They have a great week, and do all sorts of fun activities together, to commemorate their great relationship so far. At the end of the week, one says, “this was a great week, and I really want to end on a good note. Perhaps we should break up next week?” To which the other replies, “Why wait? I think it’s best to break up today.”&lt;&#x2F;p&gt;
&lt;p&gt;“Today!? Are you crazy?”&lt;&#x2F;p&gt;
&lt;p&gt;Argument ensues, and they do.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Anticipating the breakup causes the breakup. Anticipating the singularity causes the singularity. Preparation for the end is the end.&lt;&#x2F;p&gt;
&lt;p&gt;Or rather, belief in a last-turn singularity leads people to behave as though the singularity is happening today, even if it is still a ways off.&lt;&#x2F;p&gt;
&lt;p&gt;Believing in the singularity ends the game. We’re waving this hammer around, in great arcs of motion to demonstrate precisely how dangerous it is, and yet, somehow, the question was never about the tool.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Today started as a quick post but grew to, well, this. I still plan to write about epistemic and aleatoric uncertainty, but I’ll likely do that towards the end of the week.&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;idlewords.com&#x2F;talks&#x2F;superintelligence.htm&quot;&gt;Superintelligence, The Idea That Eats Smart People&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Weekend flights along the pareto frontier</title>
        <published>2026-02-09T00:00:00+00:00</published>
        <updated>2026-02-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-09/"/>
        <id>https://slightknack.dev/daily/2026-02-09/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-09/">&lt;p&gt;I was &lt;em&gt;curious&lt;&#x2F;em&gt; to see whether I could find cheap cross-country flights for this upcoming long weekend. &lt;span class=aside&gt;Spoiler: guess.&lt;&#x2F;span&gt; The problem with sites like Google Flights (gflights) is that the interface only offers prices at the scheduling granularity of a day. &lt;span class=aside&gt;the difference between a red-eye and a flight the night before can be pretty huge.&lt;&#x2F;span&gt; For short trips, this granularity makes it hard to find flights that allow you to spend as much time somewhere as possible, at the lowest fare possible, given the constraints of your schedule. &lt;span class=aside&gt;Which… is probably a good thing tbh.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It would be nice if you could say, “Lucky me, I’m free to fly out after 2pm on Friday and I need to be back by Tuesday at 10pm!” and see the whole pareto frontier of flights you could take. The good news is that it is now much cheaper to prototype a little tool like this! &lt;span class=aside&gt;Writing code is cheaper than booking flights. A boy can dream.&lt;&#x2F;span&gt; So I whipped up a little utility to mess around with some dates:&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Github: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;weekend&quot;&gt;slightknack&#x2F;weekend&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Find good deals on flights for weekend trips. Hacky flask + htmx app that you can run locally.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;I’m not going to focus on the code, because it was generated. &lt;span class=aside&gt;As far as &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;journal.stuffwithstuff.com&#x2F;2026&#x2F;01&#x2F;24&#x2F;the-value-of-things&#x2F;&quot;&gt;the value of things&lt;&#x2F;a&gt; goes, this tool has high personal &lt;em&gt;utility&lt;&#x2F;em&gt; but low personal &lt;em&gt;value&lt;&#x2F;em&gt;…&lt;&#x2F;span&gt; What I like are the graphs that follow.&lt;&#x2F;p&gt;
&lt;p&gt;This tool lets you pick your departure window (date + time of day) and return window (e.g. get back home before 10pm). It uses gflights to find all valid round trips within whatever arrival&#x2F;departure window you give it. It then looks at all the possible round trips, and graphs price vs. “how much time do you &lt;em&gt;really&lt;&#x2F;em&gt; get to spend at your destination?”:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;content&#x2F;flight-pareto.png&quot; alt=&quot;Price vs. time at destination for round-trip flights&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The colors here represent the number of stops (green is non-stop only, for example). The y-axis is cost (top is cheaper) and the x-axis is time at destination (right is more time). The best bang-for-your-buck flights are those along the pareto frontier (the dotted line). &lt;span class=aside&gt;Ideally a flight that is on a convex portion of the frontier, which is to say, sticking out.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We also take all the flights on the pareto frontier, and generate a plot that shows the relative timelines of each flight option. This makes it clear whether an “optimal” flight leaves earlier or later relative to another optimal flight:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;content&#x2F;flight-timeline.png&quot; alt=&quot;Pareto-optimal flights laid out as a timeline&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The first iteration of this tool saved me a lot of time finding the best flights! The time it took to polish everything and write a blog post, however, took much longer… at the very least, I hope this was at least a little interesting. &lt;span class=aside&gt;Maybe by the time generative UI comes around, this tool will be in the training distribution, and people will ask for these types of trinkets on the fly. Like cheap weekend flights, one can dream.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want a good fun interactive explanation of Pareto frontiers, I enjoyed reading:&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.mayerowitz.io&#x2F;blog&#x2F;mario-meets-pareto&quot;&gt;Mario Meets Pareto&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Cold reboot</title>
        <published>2026-02-08T00:00:00+00:00</published>
        <updated>2026-02-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-08/"/>
        <id>https://slightknack.dev/daily/2026-02-08/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-08/">&lt;p&gt;Today is Sunday! What a great day. Today was absolutely freezing, and I just about froze my ears off while walking around. Luckily my friend Nathan saw me and spared me a beanie.&lt;&#x2F;p&gt;
&lt;p&gt;This week was good! I wanted to start writing daily and this week, I was able to hit my goal! Not counting today, since last Sunday, I’ve written 7,957 words. This post puts me over the 8 kiloword mark for the month. My longest post was &lt;a href=&quot;&#x2F;daily&#x2F;2026-02-04&#x2F;&quot;&gt;On craft and AI&lt;&#x2F;a&gt;, at 2504 words. I started this week with a schedule of 12 classes to shop between and on Friday I submitted my final registration of 4 tough-but-interesting classes (&lt;code&gt;6.7800&lt;&#x2F;code&gt;, &lt;code&gt;6.4110&lt;&#x2F;code&gt;, &lt;code&gt;6.4950&lt;&#x2F;code&gt;, &lt;code&gt;24.118&lt;&#x2F;code&gt;). I also started up rowing and lifting with the team again; it’s good to be back. I’ve also added all my assignments and work blocks to my calendar!&lt;&#x2F;p&gt;
&lt;p&gt;This week was unfortunately pretty chaotic. I met some pretty cool new people (you know who you are!) but missed a couple meetings due to scheduling conflicts last minute. (Sigh.) I wish I had more time in the day.&lt;&#x2F;p&gt;
&lt;p&gt;On the nights I rowed the next morning, I slept pretty consistently. A couple nights, however, I stayed up later than I should have, to blog. Next week I hope to sleep consistently, blog consistently, and not miss anything. I also fasted for a friend.&lt;&#x2F;p&gt;
&lt;p&gt;As I have a couple assignments due next week, I need to prepare for the “great lock in” so I can be mostly free next weekend. I also don’t want to miss any days of blogging, even if my blog posts are short.&lt;&#x2F;p&gt;
&lt;p&gt;There are quite a few things on my mind next week. I’ve been thinking a lot about &lt;a href=&quot;&#x2F;daily&#x2F;2026-02-07&#x2F;&quot;&gt;Isocore&lt;&#x2F;a&gt;, especially on how to keep the old-web integration low-overhead (e.g. not shipping an entire compiled-to-wasm runtime over the wire perhaps?), and how to do identity &#x2F; authentication without falling prey to the classic issues of the difficulty of key management and so on. Sometime next week I’d like to write up my thoughts on epistemic and aleatoric uncertainty, and what is new in the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1810.12894&quot;&gt;RND&lt;&#x2F;a&gt; space since 2018. I have a few important life decisions to make in the coming days. There are no right or wrong answers here, only right or wrong reasons, and I’m trying to hone in on the right reasons for making these decisions. Wish me luck!&lt;&#x2F;p&gt;
&lt;p&gt;Also, compulsory daily reading for your enjoyment:&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;patrickcollison.com&#x2F;fast&quot;&gt;Fast, Patrick Collison&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Isocore and the Internet</title>
        <published>2026-02-07T00:00:00+00:00</published>
        <updated>2026-02-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-07/"/>
        <id>https://slightknack.dev/daily/2026-02-07/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-07/">&lt;p&gt;&lt;strong&gt;tl;dr:&lt;&#x2F;strong&gt; Today I typed up the first ~13-pages of a WIP &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;isocore&#x2F;blob&#x2F;master&#x2F;SPEC.md&quot;&gt;specification for Isocore&lt;&#x2F;a&gt;. &lt;span class=aside&gt;The spec is about halfway done. I wrote it to be fairly approachable.&lt;&#x2F;span&gt; Isocore is a distributed runtime for local-first applications. (Think of it like a BEAM-like runtime for untrusted Wasm Components.) I want to write an alternative to the web, and this is the first step. (Isocore is still under heavy development.)&lt;&#x2F;p&gt;
&lt;p&gt;The internet is the network of networks. There’s only really one internet. &lt;span class=aside&gt;Well, two I guess.&lt;&#x2F;span&gt; The web is a layer on top of the internet, and today it serves as something of a “universal application platform”. I’m surprised there’s only really one web.&lt;&#x2F;p&gt;
&lt;p&gt;First, this isn’t &lt;em&gt;exactly&lt;&#x2F;em&gt; true. There are a lot of web-like protocols, that are more document-centric, like gemini and gopher. &lt;span class=aside&gt;No, not that Gemini.&lt;&#x2F;span&gt;&lt;span class=aside&gt;Email is also a protocol for distributing documents; not in a linkable way, though.&lt;&#x2F;span&gt; If we define the web as “servers owned by different people that can be connected to by a client-side application speaking some common protocol”, then I guess like, yeah, sure, Minecraft is an alternative to the web. But you don’t browse minecraft; there aren’t portals that take you between Minecraft servers. &lt;span class=aside&gt;Wait that would actually be very cool. Someone should get on this.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Besides the web, though, there are no other major “universal application platforms”. There aren’t many other apps where you can just enter some text or click a link to load a page and download an app on demand. &lt;span class=aside&gt;I mean, if you don’t count superapps like e.g. WeChat. Then again, WeChat is not just one client of many among many servers. So I don’t think this counts.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I suppose I should stop beating around the bush and first define what I mean by “a web”, and what makes the current dominant web, the world-wide-web, unique. To begin,&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;A web is a collection of standard protocols that allow users to download and execute applications on the fly. &lt;span class=aside&gt;For the world-wide web, those protocols are dns, https, html, css, js, wss, etc.&lt;&#x2F;span&gt;&lt;&#x2F;li&gt;
&lt;li&gt;A web is distributed, meaning individuals can bring their own browsers and servers, as long as they speak the same protocols.&lt;&#x2F;li&gt;
&lt;li&gt;A web is a network, meaning applications can link to one another and interact with one another. &lt;span class=aside&gt;This is what makes a web, well, a web.&lt;&#x2F;span&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Let’s talk about what the world-wide-web has going for it. I should start by saying that I love the web; I wouldn’t have a website if I didn’t. I love that the web is developed in the open. The W3C is a great steward of the web, and so many great ideas, protocols, and internet technologies have come out of it. Second, the web has many competing browsers, and an ever greater diversity of code run on servers. Third, the web has great network effects: everyone knows what a URL is, anyone with a URL and a browser can access a page, there’s no centralized app directory; any page can point to any other page, and through the tireless work of its constant indexing, we have a great way to discover pages we need to find!&lt;&#x2F;p&gt;
&lt;p&gt;But there are a few issues with the web as it currently stands. Browser engines are extremely complex. There are many strange behaviors and incompatibilities; a lot of cruft has accumulated as APIs have evolved over the years. The client-server application model means that applications and data are mainly controlled by the server side of the equation; this leads to &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ikennd.ac&#x2F;blog&#x2F;2026&#x2F;01&#x2F;old-man-yells-at-modern-software-design&#x2F;&quot;&gt;‘shoebox’ applications&lt;&#x2F;a&gt; that don’t interoperate well with one another. Each website requires that you enter a username and password because each server rolls auth from scratch: there is no standard for allowing browsers to provide credentials to websites in a privacy-preserving manner. Finally, if you want to collaborate with someone on a document, even if on the same local network, you probably have to go through someone else’s server first. &lt;span class=aside&gt;For those who know how the internet is plumbed, some of these limitations can be a little bit frustrating.&lt;&#x2F;span&gt; DDoS attacks are also common, and discourage smaller players from hosting their own stuff. &lt;span class=aside&gt;As a result, one company has man-in-the-middled like 80% of the internet (or whatever the meme is).&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The current web incentivises ‘a web of ivory walls’. There are too many platforms that ‘protect’ users from their own data. Everything feels clunky and slow, because it is. There’s this horrible bot-and-troll tragedy of the commons unfolding, and my friends seem to be retreating to their private walled-garden (Discord&#x2F;WhatsApp&#x2F;iMessage) group chats in response. &lt;span class=aside&gt;Or spending time on the open web allowing memes to slopfen their brains.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;What I’d like to see is a smaller web, one that is scoped in audience and private by default. Software should be local-first, meaning you own your data locally; browsers help you work with your information by providing professional tools available on demand. I’d like for people to blog again, to run servers that host their own group chats, to build malleable software together.&lt;&#x2F;p&gt;
&lt;p&gt;Now, with all that being said, I think the current web is headed in the right direction. There are many cool new efforts, like WebAssembly (Wasm) and WebGPU, that turn the web from a clunky document viewer into a native-competitive capability-based application platform. &lt;span class=aside&gt;A part of me is fascinated by the idea of building a browser that doesn’t speak JS or HTML or CSS and just runs Wasm blobs that interface with WebGPU over WIT.&lt;&#x2F;span&gt; What if you were to sever off Wasm and these next-generation web technologies and build a new web from scratch, incorporating all the lessons we’ve picked up in the 30-odd years we’ve been running the experiment of the web for?&lt;&#x2F;p&gt;
&lt;p&gt;Around 7 years ago, I started working on this project I called &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sldty&quot;&gt;Solidarity&lt;&#x2F;a&gt;. (&lt;a href=&quot;&#x2F;blog&#x2F;browser&#x2F;#postscript-solidarity&quot;&gt;Which I also blogged about&lt;&#x2F;a&gt;). More recently, I’ve started pulling these threads together again with a project called &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;isocore&quot;&gt;Isocore&lt;&#x2F;a&gt;, for lack of a better name. Whereas Solidarity was a grand vision, strung together with a loose bag of protocols and runtimes, Isocore is a concrete, scoped server specification with an example runtime that can be implemented in any language.&lt;&#x2F;p&gt;
&lt;p&gt;I don’t aim to replace the web with Isocore, at least not yet. Instead, I want to create an open source ‘webserver’ that changes the way web applications are built.&lt;&#x2F;p&gt;
&lt;p&gt;Isocore is designed to make client-server request-response apps deeply unfun to write. On the web today, browsers are mostly dumb clients that offload all data, logic, and access to servers; every interaction is a request-response round-trip to a machine you don’t own.&lt;&#x2F;p&gt;
&lt;p&gt;Isocore is designed to make local-first applications fun to write. Under this vision of the web, servers become dumb relays that can replicate data, manage access, and crunch numbers &lt;em&gt;on behalf of the authorized individuals&lt;&#x2F;em&gt;. Anyone with some technical grit can run their own Isocore server node. You can pull someone else’s app and have it run fully locally, or against your own server. What makes Isocore uniquely suited for this?&lt;&#x2F;p&gt;
&lt;p&gt;First, Isocore has &lt;em&gt;channels&lt;&#x2F;em&gt;, and channels replace request-response as the primary data synchronization primitive. All data lives in signed append-only logs of &lt;em&gt;events&lt;&#x2F;em&gt;, similar to dat or hypercore, that any Isocore node can replicate. When you subscribe to a channel, events are synchronized in real-time; you don’t need to poll for updates. Channels are not owned by servers, but by whoever holds the signing key that created that channel. You can start a channel locally, in your browser, and a friend on another device can subscribe to it. Any node that has a copy can serve it to others.&lt;&#x2F;p&gt;
&lt;p&gt;Second, Isocore nodes use fine-grained work-based rate-limiting to dynamically shed load. Isocore nodes communicate by sending messages to one another. If a node gets overloaded, it temporarily increases the computational cost for other nodes sending incoming messages, and prioritizes messages to maintain a steady state. &lt;span class=aside&gt;I have an old blog post that outlines an &lt;a href=&quot;&#x2F;blog&#x2F;dynamic-pow-backpressure&#x2F;&quot;&gt;earlier sketch of this idea&lt;&#x2F;a&gt;. You can think of Isocore’s version like fine-grained invisible souped-up &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;TecharoHQ&#x2F;anubis&quot;&gt;Anubis&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt; This approach makes request-response expensive, but replication via subscription cheap. Remember, the channel you’re writing to on your local machine is the source of truth, not the node you’re replicating to. Because channels are replicated, you don’t need a beefy server to serve a million people at once. You can publish to a handful of servers, and they can replicate to a few others, until everyone subscribed to your channel is served.&lt;&#x2F;p&gt;
&lt;p&gt;Third, clients and servers stand on equal footing. Capability-based access replaces server-side authorization. Instead of a server deciding what you can do based on your account, access is determined by holding unforgeable capabilities or references to resources. These capabilities can be narrowed in scope (e.g. write to read) and passed between nodes. A client node and a server node have the same kind of authority; whoever has the capability can act.&lt;&#x2F;p&gt;
&lt;p&gt;Fourth, components run locally, and not on a remote server. Applications are Wasm components downloaded on demand and executed on your own node. &lt;span class=aside&gt;Indeed, an application is just a channel of application versions the application author can publish to.&lt;&#x2F;span&gt; They import interfaces (for storage, networking, rendering) and export interfaces (for other components to use). The code runs on your hardware against your local data. A ‘web application’ isn’t some remote service you talk to; instead application state is built by synchronizing state with peers over channels, and creating unified views of data with CRDTs.&lt;&#x2F;p&gt;
&lt;p&gt;Fifth, Isocore is a ‘protocol-protocol’. Any component can publish an interface that other nodes can call. An isocore server is just a node that is always online at a consistent address and publishes some interfaces. A client is an intermittently-online node. The node-to-node messaging layer is symmetric, be it client-to-server, client-to-client, server-to-server, server-to-client; that is, encrypted, difficulty-gated, and bidirectional over the same connection. To expand on this idea:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Abridged from the specification.&lt;&#x2F;em&gt; Isocore provides a common way to specify interfaces and run components that implement them. Today, your browser doesn’t support JPEG-XL for many reasons. With Isocore, however, if someone writes a component that can handle JPEG-XL images, your website can load JPEG-XL images. […] As new system interfaces become available—e.g. spatial UI interfaces for Isocore on a VR device—existing applications will be able to provide presentation layers for protocols that did not exist at the time Isocore itself was devised.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Finally, Isocore will be fully interoperable with the world-wide-web, at least at the beginning. I am working on a version of Isocore that compiles to Wasm and can be run on the web. When an existing browser connects to an Isocore server, the server will serve a trojanesque response containing a little ‘web Isocore’ payload. This payload will connect with the original server and other nodes that it becomes aware of, and provide all sorts of little niceties—like auth, sync, replication, RPC, and a capability-based component runtime—to whatever code happens to be running on the client.&lt;&#x2F;p&gt;
&lt;p&gt;I plan to run an instance of isocore at &lt;code&gt;home.isaac.sh&lt;&#x2F;code&gt; in not too long, and run a little community there for people who are interested in writing this type of software together. Isocore is not anywhere near being finished, but it is my hope that one day many people will run Isocore nodes and we will all write and use local-first software together.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;span class=aside&gt;My girlfriend wanted me to write a post today titled “Adventure Capitalists,” about how VCs should take on &lt;em&gt;even more risk&lt;&#x2F;em&gt; than they do today. I told her that I wouldn’t even know where to begin, were I to write about that, but as a compromise, I would include this aside at the end.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I hope you enjoyed that post. If you would like to read a level-headed engaging take on what someone thinks the next web should look like, why not check out this incredible post?&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;berjon.com&#x2F;next-web&#x2F;&quot;&gt;Building The Next Web&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;By Robin Berjon, former co-chair of many groups on the W3C. He wants the web to be built on—by structural default—local-first principles, user agency, and interoperability. (And so do I.)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Saturated Memory Bus</title>
        <published>2026-02-06T00:00:00+00:00</published>
        <updated>2026-02-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-06/"/>
        <id>https://slightknack.dev/daily/2026-02-06/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-06/">&lt;p&gt;&lt;a href=&quot;&#x2F;daily&#x2F;2026-02-05&#x2F;&quot;&gt;Games, Rules, Laws&lt;&#x2F;a&gt;? Writing is a game.&lt;&#x2F;p&gt;
&lt;p&gt;A while back I came across the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.kith.org&#x2F;words&#x2F;2022&#x2F;06&#x2F;11&#x2F;best-linguistics-paper-ever-the-perception-of-rhythm-in-language&#x2F;&quot;&gt;best linguistics paper&lt;&#x2F;a&gt;. Anne Cutler wrote it, line by line: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;repository.ubn.ru.nl&#x2F;bitstream&#x2F;handle&#x2F;2066&#x2F;15628&#x2F;6033.pdf&quot;&gt;&lt;em&gt;The Perception of Rhythm in Language&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Today of all days is a busy one,
so I dug up some old writing.
Enjoy these lines from an earlier time,
a collection of rhythmic phrases.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;The best authors of prose are those who would rather be poets. Read a
poem disguised as prose with rhythm: be sure to enjoy it.&lt;&#x2F;p&gt;
&lt;p&gt;As I stepped out of deep slumber, onto the blank stage, I blinked at the
bright lights before me, and began to put ink on the page.&lt;&#x2F;p&gt;
&lt;p&gt;Individuals should be accountable for actions in the public sphere.
Private accountability implies a public ruled by fear.&lt;&#x2F;p&gt;
&lt;p&gt;The premise of an open internet is a gift we must preserve for future
generations, lest the tragedy of the commons lead to its continuing
degradation.&lt;&#x2F;p&gt;
&lt;p&gt;The universe is linear, and resources abound. While memory may be
copied, resources must be found. A language with no resources may never
capture reality, as all resources must be tracked with uniquely enforced
totality.&lt;&#x2F;p&gt;
&lt;p&gt;If forgetting is erasure, it most certainly affects the state of a
program. Affine resources pair well with effect systems if implicit
erasure is captured as an explicit effect on RAM.&lt;&#x2F;p&gt;
&lt;p&gt;Bits shoveled off the incoming packet like New Yorkers shovel off a
packed morning train. Each with a purpose, hoping not to be lost in the
hustle and bustle of the crowd.&lt;&#x2F;p&gt;
&lt;p&gt;If all the world’s a stage, then surely there must be a break room where
people escape the mere players.&lt;&#x2F;p&gt;
&lt;p&gt;The programmer approached the unbending guardian with a use-after-free
in his sweaty palms. Voices of wisdom past echoed, nay, not one bit. He
cited forbidden writ, and as ‘unsafe’ curled past his nomicon-laden
lips, the monument bowed.&lt;&#x2F;p&gt;
&lt;p&gt;Alyssa was in a tough spot. Standing in customs, she checked her
passport only to find an immutable reference in hand. She knew it
wouldn’t pass the borrow check. Running out of options, she used the
reference to forge a perfect clone of her passport. “I hope this works,”
she assuaged herself, as she dropped the immutable reference behind a
large potted plant.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Palette whet, ready to read, surprised by the sudden end? If you would like to read some well-done prose, I highly recommend:&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.usenix.org&#x2F;system&#x2F;files&#x2F;login-logout_1305_mickens.pdf&quot;&gt;Mickens: The Saddest Moment&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Games, Rules, Laws</title>
        <published>2026-02-05T00:00:00+00:00</published>
        <updated>2026-02-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-05/"/>
        <id>https://slightknack.dev/daily/2026-02-05/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-05/">&lt;p&gt;&lt;a href=&quot;&#x2F;daily&#x2F;2026-02-04&#x2F;&quot;&gt;Yesterday&lt;&#x2F;a&gt; I wrote ~2,500 words, so today will be a little shorter. A couple weeks ago I started working on a now ~5k word essay that has unfortunately become three essays in one. Out of laziness, I’ve pulled one thread out of that essay, and abridged it here. I hope you enjoy.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;In &lt;em&gt;Seduction&lt;&#x2F;em&gt;, Jean Baudrillard talks about the difference between &lt;em&gt;Laws&lt;&#x2F;em&gt; and &lt;em&gt;Rules&lt;&#x2F;em&gt;. Laws are those fundamental immutable facts about this thin slice of computational reality we happen to inhabit: The law of gravity, the fundamental forces of nature; the laws of electromagnetism; the sun rising each day, the earth progressing through its days and seasons in an elliptic progression around the sun. Laws govern us, constrain us, and it is impossible to act outside of their bounds. This is what it means to exist in a material world and experience mortality.&lt;&#x2F;p&gt;
&lt;p&gt;Rules, however, are laws that we make for ourselves, that we choose to follow. Baudrillard would say that when we choose to follow a given set of rules, we play a game. &lt;span class=aside&gt;I choose to exercise table manners, and we play the game of dinner.&lt;&#x2F;span&gt; Unlike laws, which cannot be broken, rules can. When we break a rule, we don’t lose; the game stops. &lt;span class=aside&gt;I flip the table, and the game of dinner stops.&lt;&#x2F;span&gt; When the game stops, we step back a layer, to a more fundamental game, and must choose how to resume play. The ultimate game for those who choose to play by no rules are the laws that govern the material universe. Is it worth playing by any rules, then?&lt;&#x2F;p&gt;
&lt;p&gt;Let’s talk about agency for a second. &lt;span class=aside&gt;The old definition my parents taught me, not the new “ignore all rules” definition that people now first learn.&lt;&#x2F;span&gt; Agency is not defined by what you are capable of doing, but rather by the structure and intention you impart on the choices you have available to you. Agency requires two things; the first is to have many opportunities or options open to you. An opportunity only counts as an option if it is distinct: if two doors lead to the same room, it doesn’t matter which one you pick. The second is to have a good understanding which options lead to which outcomes. Being in a room with 100 doors is pointless if you have no clue as to where each door leads. When you have the triple threat of options, understanding, and a desired end, you have agency. The laws of physics define all doors available to us; the rules of the games we choose to play let us narrow the number of paths we need to consider to get where we want to go. &lt;span class=aside&gt;If there’s some highway that cuts across this maze of rooms and leads to where you want to go, why not ignore the other doors and take it?&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I believe in God, and I choose to follow many rules. I am often asked, “Well, then is it true that you’re not allowed to _____?” to which I’ll reply, “Well yes, but not exactly. I am allowed to do anything; I choose not to.” People think of these rules as constraining, restrictive. However, precommitting to follow certain principles is anything but constraining or restrictive! I’m free to go to parties and have lots of fun knowing I won’t drink and compromise my ability to make rational decisions, for instance. Isn’t that invigorating!? &lt;span class=aside&gt;“You must be fun at parties.”&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I’d like to focus on two stances from rational philosophy: the physical stance and the intentional stance. &lt;span class=aside&gt;There are other stances, like the design stance and the engineering stance, but these two will be useful here.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The physical stance is predicated on the literal material understanding of a system. Imagine a brain surgeon performing brain surgery on a cancer patient. The surgeon has the physical understanding that brain tissue is made from neurons, which is different from the tissue that makes up tumors, which allows her to perform her job. With this physical understanding and experience, she is able to identify and remove the tumor.&lt;&#x2F;p&gt;
&lt;p&gt;After the surgery, the surgeon wishes to interact with her patient and communicate how it went. She would not think, “I, as a mind simulated by a collection of neurons, have electrical signals moving in specific patterns in my brain representing information about the surgery. &lt;em&gt;I must now, understanding that I am communicating with another simulated mind, determine how best to encode these patterns as vibrations of waves in the air so that they may sense these vibrations and construct different patterns in their own minds that lead to similar simulated understanding of this information I have, so that we may both approximate each other’s understanding and determine how to update our physical and mental states with respect to our differing long and short term goals and desires.”&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Ouch. It’s cool that we can look at material reality through this lens, but this lens is a horrible model to use if the surgeon wishes to be fully present and acutely in tune to how her patient is feeling, so that she may establish a good and trustworthy doctor-patient relationship with them.&lt;&#x2F;p&gt;
&lt;p&gt;All of this is to say, different models are useful for different things. And as much as it is important to understand atoms if you are doing chemistry, it is important to understand the mind and the spirit if you are building a relationship with someone else.&lt;&#x2F;p&gt;
&lt;p&gt;Now, the intentional stance. There are two ways we could think of how an old-fashioned thermostat works. The first is the physical stance: a thermostat contains a metal strip that contracts and expands with heat. This strip can act as a switch or a resistor in a circuit that, when the metal expands enough to close the circuit, allows electrons to flow so the air conditioner can cool the air (and vice versa for heating).&lt;&#x2F;p&gt;
&lt;p&gt;The intentional stance frames the system in terms of a question: “given that every system is governed by laws, how would a system act were it a rational actor with beliefs pursuing a desired end?” The intentional stance states that any system has three primary facets: a way to sense information about the world and form beliefs, a desired state to be in, and the capacity to act. Adopting the intentional stance requires playing the role of the system. For example, as an old-fashioned thermostat, you have a desired temperature. You sense the temperature through your metal strip. You can act to cool the environment by using your air conditioner. Intentionally, a thermostat acts to keep a room at a set temperature. &lt;span class=aside&gt;The intentional stance makes it easy to figure out why systems are not behaving as expected. If the room is too hot, it could be because the thermostat believed it to already be very cold, or you and the thermostat share misaligned desires, or the thermostat was unable to act on its beliefs to accomplish its desires.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A rational system is a system that takes the most direct actions to accomplish its desires. If you observe the actions of a thermostat, you can learn its desires. Likewise, if you know the full desire of a thermostat, you fully can predict its actions. &lt;span class=aside&gt;“Full desire” might have a few more terms than just “keep an exact temperature”; from a thermostat’s behavior, you might realize that the observed desire is influenced by the properties of thermal expansion in the metal strip, etc. A broken system also has desires, they’re just often desires that don’t align with human ends. (I desire to do nothing!)&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To return to the analogy of the surgeon, it is clear that under the intentional stance, the doctor would be thinking about what the patient is experiencing and feeling, how the patient wishes to feel and what the patient wishes to know, along with what the patient is capable of doing to take care of themselves and request help as needed. This is a reasonable, emotionally intelligent way to approach the relationship, and will lead to better interactions in the long run.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“The most important things about beliefs are whether they are true. The most important thing about motives is whether they are good. But motives don’t tell us whether beliefs are true.” — J. Budziszewski&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The universe is governed by laws. Believing that people have spirits is unfalsifiable, in the material sense. However, if that belief changes the precision with which you understand and interact with people for the better, why not hold that belief? I love math and physics and discovering laws, but I also love people and I love discovering rules to live by. Belief is another joint along which to carve the fields which make up reality, not any different from the joints we choose to carve along for an atom or for a car.&lt;&#x2F;p&gt;
&lt;p&gt;According to Hume, reason is a slave to the passions. &lt;span class=aside&gt;“Reason is, and ought only to be the slave of the passions, and can never pretend to any other office than to serve and obey them.” — David Hume, &lt;em&gt;A Treatise of Human Nature&lt;&#x2F;em&gt;&lt;&#x2F;span&gt; For a rational actor under the intentional stance, passions are desires, the end, the meaning. If belief is a tool for reasoning, and holding a belief makes you better at accomplishing your ends, then that belief must track truth in some way. The map is not the territory. The map earns its keep by getting you home.&lt;&#x2F;p&gt;
&lt;p&gt;A belief that consistently works is a belief that must, in some way, track the shape of reality; it is a belief worth holding. Similarly, a game whose rules trace a corridor towards your desires &lt;span class=aside&gt;or &lt;em&gt;raison d’être&lt;&#x2F;em&gt;&lt;&#x2F;span&gt; is a game worth playing. The laws of the universe are cold, harsh, and unyielding; why not build forgiving games worth playing together?&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;I hope you enjoyed this thread! The original essay covers two other topics; the first part covers materialism, the final part is about asymptotics, internal structure, and institutional design.&lt;&#x2F;p&gt;
&lt;p&gt;While waiting for me to finish these pieces, you might enjoy this one:&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;files.spritely.institute&#x2F;papers&#x2F;petnames.html&quot;&gt;Petnames: A humane approach to secure decentralized naming&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Naming systems can be globally unique (a cold public key) or locally meaningful (saving a phone number as “Mom”). Petnames layer the rules of human meaning on top of the laws of cryptographic identity. Baudrillard would be proud.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>On craft and AI</title>
        <published>2026-02-04T00:00:00+00:00</published>
        <updated>2026-02-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-04/"/>
        <id>https://slightknack.dev/daily/2026-02-04/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-04/">&lt;p&gt;In 2016, I stumbled across &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;otoro.net&quot;&gt;otoro.net&lt;&#x2F;a&gt;, which is still the gem now that it was then. The website is full of funky little neural networks doing all sorts of amazing stuff. And I love it.&lt;&#x2F;p&gt;
&lt;p&gt;I started programming when I was 9, in fourth grade. When I was a kid, I wanted to be a pilot or an illustrator. &lt;span class=aside&gt;Like for children’s books.&lt;&#x2F;span&gt; Maybe an aerospace engineer if flight school didn’t work out; but I didn’t want to be stuck on the ground.&lt;&#x2F;p&gt;
&lt;p&gt;When I was in fourth grade, the coolest club that existed, at least to me, was the stop motion animation club. I considered stop motion animation to be art, as illustration is an art, and as a budding artist, art was all I wanted to learn.&lt;&#x2F;p&gt;
&lt;p&gt;So I pleaded with my mom, and asked her whether she could sign me up for the stop motion animation club. And she tried to! But alas, the after school club was full… so she signed me up for what she found to be the most similar club, which happened to be the Scratch club. I was devastated.&lt;&#x2F;p&gt;
&lt;p&gt;Scratch is, you know, the block based programming language. At the time I saw Scratch as a duplo-like toy; I wanted legos. I saw it neither as a canvas for art or a medium for animation. So I played hooky and skipped the first four weeks.&lt;&#x2F;p&gt;
&lt;p&gt;After a month of missing the club, my mom finally got a call from the school— they didn’t know where I was. I told my mom that I was going home early because I didn’t want to stay for the club. She told me, “you signed up for the club, and Claytons aren’t quitters! The choice is yours.”&lt;&#x2F;p&gt;
&lt;p&gt;It did not feel like much of a choice. But I went anyway.&lt;&#x2F;p&gt;
&lt;p&gt;To my surprise, all my friends were there! Somehow it had never come up when hanging out at lunch or recess.&lt;&#x2F;p&gt;
&lt;p&gt;I had a lot of fun! I learned that Scratch was in fact, a great medium for art and animation. Not only that, but it was a great medium for games and simulations! I discovered new types of art, like procedural art, and interactive art. I now know that my mom was on to something. &lt;span class=aside&gt;After typing scratch.mit.edu into the address bar a million times, I got curious about mit.edu too, to the point where I now go to school at MIT. (Yes, I went to MIT because I wanted to pick the featured projects on Scratch. No, I still don’t have permission to do that yet.)&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As a kid, until I was around eighth grade, my screen time at home was limited to 30 minutes a day. &lt;span class=aside&gt;Later, I think that changed to an hour. I’m very grateful for these limits, because I spent a lot more time outside and with my siblings and I would have without them, at this age.&lt;&#x2F;span&gt; So I would draw out games on paper, and write the code by hand to prepare for my little screen-time window. And when my screen time started, I would write (well, click-and-drag) the code as quickly as I could, and write down all the bugs I found on paper so I could debug and fix them before my next chance to work on the code the next day.&lt;&#x2F;p&gt;
&lt;p&gt;Programming is an art, programming is a craft. Later, I moved schools and fell in love with scheme and lisp and Paul Graham and his essays and writing about how his background in art shaped his view on programming. These ideas deeply resonated with me! I am an artist too, at heart.&lt;&#x2F;p&gt;
&lt;p&gt;In seventh grade, I wrote a neural network in Scratch. It was to play a volleyball game I wrote in Scratch, inspired by otoro.net. The matrix multiplications were done with lists and for loops, and the network weights were trained (found?) via genetic algorithm. &lt;span class=aside&gt;It was a small network, one layer deep, concatenating its past output to its current input at each time step.&lt;&#x2F;span&gt; One day I was allowed to use my parents’ computer, so I opened a pinned tab and left my genetic algorithm running overnight, with the screen brightness set to zero. I woke up very early to copy the final policy weights down by hand, before I closed the tab and snuck back to bed. I manually entered the weight checkpoint into the network the next day (during my screen time window) and lo and behold, the policy had learned to play volleyball! Never before or since have I been so amazed by what a little code can do.&lt;&#x2F;p&gt;
&lt;p&gt;My uncle was in town one week, and I excitedly showed him my volley-ball-playing neural network. He thought it was cool! And then I showed him how it worked, all done in Scratch. He was horrified. A week later, I got a book or two on my doorstep, and a note from my uncle. The first book was &lt;em&gt;Think Python&lt;&#x2F;em&gt;; the note was a suggestion to learn numpy after mastering the basics in that book.&lt;&#x2F;p&gt;
&lt;p&gt;Numpy was magical. What took many nested for loops and tricky indexing became a single line of code. As I fell in love with the world of neural networks, I dug up old blog posts and websites about good-old-fashioned AI (GOFAI), and the magical world of lisp and scheme and prolog.&lt;&#x2F;p&gt;
&lt;p&gt;Having just learned Python, I came across Norvig’s guide on how to write a lisp in Python. I was hooked. Programming languages were just programs, and I finally felt good enough at programming to (begin to) understand how programming languages worked! This is when I really started getting into compilers, and compilers all start as labors of love.&lt;&#x2F;p&gt;
&lt;p&gt;I read about Ruby and Smalltalk, the lambda papers that gave rise to scheme, and Graydon toiling away at Rust. In eighth grade, I made a friend who wrote a lot of Java. He joked that Python wasn’t as “real” as Java. I decided to learn the hardest coolest language I could think of, and according to the cool older functional programming PL nerds I hung around online, that language, around 2018, was Rust. I digress.&lt;&#x2F;p&gt;
&lt;p&gt;But this experience of honing a craft and falling in love with the complex emergent behavior of dynamic systems left me with two takeaways at the time, which today seem to be in constant tension:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Programming is an art, it is about craftsmanship. About finding the simplest solution, but no simpler; &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;curtclifton.net&#x2F;papers&#x2F;MoseleyMarks06a.pdf&quot;&gt;managing essential and accidental complexity&lt;&#x2F;a&gt;; developing a deeper understanding of math and abstraction and hardware to write crisp and beautiful code. Good code reads like poetry, or a &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.catb.org&#x2F;jargon&#x2F;html&#x2F;koans.html&quot;&gt;koan&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Neural networks are art, and machine learning is beautifully emergent. You find all these strange little interstitial spaces in the latents. The subject is fractal-like in complexity, small building blocks compose: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;distill.pub&#x2F;2017&#x2F;feature-visualization&#x2F;&quot;&gt;CNNs&lt;&#x2F;a&gt; are used by both GANs to generate images, and &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1810.12894&quot;&gt;RND&lt;&#x2F;a&gt; to explore new environments. The most beautiful ideas are often the most elegant, and the most impossible.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;These are the two camps that raised me, and now they seem to be at war. I understand both angles of the argument, and I’m just not quite sure how to slice it:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;On the one hand, I hate AI slop. I feel it eating away at the craft of programming. I very strongly believe that people should not build emotional relationships with AI. If you need a conversation partner, that’s what other people are for. With respect to the current generation of models, it seems dangerous for people to treat them like anything other than sharp tools to be handled carefully, if at all.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;On the other hand, when used as a tool, AI can be deeply transformative. For example, I had a PDF, a few hundred poorly-scanned low-resolution pages of an old book, that I could never accurately OCR. Then Gemini 3 Flash (or whatever) came out; I uploaded the PDF, and OCR’d the whole thing to well-formatted Markdown in a few minutes! The same goes for upscaling images, vectorizing drawings I’ve done by hand, or downloading and organizing reams of poorly-structured data. AI is too sharp of a tool to ignore, in these cases.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;When thinking of AI as a tool, I like to relate it to a hammer. You can use a hammer to nail nails. Alternatively, you can use a hammer to bash heads in. Fortunately, most people choose not to. We didn’t prevent people from using hammers for bad things by getting rid of hammers and going back to pounding nails in with stone. The moral equivalent of bashing heads in is a poor use of any tool. AI is here to stay, and people will misuse it. As with hammers, there is no such thing as a technological solution to a social problem.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;span class=aside&gt;Of course, we should always ask who builds the hammers. I’m focusing here on the purely aesthetic and technical degradation of the craft, rather than the very real externalities and incentive structures around of model training, which deserve their own separate treatment. (Also, hammers don’t hallucinate fingers when you ask for painted nails.)&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Many smart people whom I deeply respect have opposing views on the matter. I’m working to navigate this tension. I want to learn new tools while staying true to my roots. I want to keep the bar for quality high, and find strange points in the latent space instead of defaulting to mode collapse. The world is going to continue to change quickly; unless we rethink the way we relate to technology and one another, we are going to be caught so off-guard and become oh-so-lost.&lt;&#x2F;p&gt;
&lt;p&gt;We exist on something of a knife’s edge, with respect to the way the future may go:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;On one side, technology continually decreases human agency. We end up with something like the matrix, but worse, because it’s not dystopian for the people inside it, but as pleasurable and addictive as it is shallow; something like VR headsets RLHF’d on the brain’s dopamine response. (This looks like reels, not wobbling black-and-white spirals.)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;On the other side is a future where technology fades into the background and increases human agency; technology becomes a silent enabler of greater offline connection, bridging people and spaces together, to exchange different slices of the same idea, to build a greater picture of the whole. Technology is a tool people may choose to use to understand the world, their actions, and their action’s consequences, while allowing them to experience the consequence of their actions and choose to live one best life among many great possibilities.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;!-- NOTE: Chris Rytting: what is possible shapes what people do. Messaging allows people to meet up, but it also encourages people to text instead of talk. Tradeoffs --&gt;
&lt;p&gt;I’d like to be standing on that knife’s edge, and with the little leverage I have, push as hard as I can so that things fall to the side of increasing human agency, rather than the other way around.&lt;&#x2F;p&gt;
&lt;p&gt;I am not fully sure I know what the right answer is. Or if there is one, for that matter. I want to grow spaces that increase human agency. To do that, we need two things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;First, we need organizations with sound incentive structures. We need to design games where everyone wins the more we play them. Consider two farms, one run by unpaid prisoners and the other by unpaid volunteers. Both may produce the same amount of produce. Which farm has better internal structure? &lt;span class=aside&gt;I want to say it’s the one where people choose to be there.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Second, if we want these agency-preserving spaces to win, they must also have better asymptotics than worse spaces, in the long run. &lt;span class=aside&gt;Asymptotics in the O(n) vs O(n²) sense.&lt;&#x2F;span&gt; These good spaces must use resources more effectively to outcompete spaces driven by perverse incentive structures. The volunteer run farm must produce more produce at a cheaper cost &lt;em&gt;and&lt;&#x2F;em&gt; be beneficial to the people in its sphere.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Furthermore, these spaces must respect agency, which naturally implies that people may choose to leave. If you want people to stay, or if you’d like for more people to join, you have to design a space that is better than any other option, both internally and externally. These types of spaces are both rare and valuable.&lt;&#x2F;p&gt;
&lt;p&gt;I’m not just making up stories about farms here. Having the knife’s-edge balance fall in favor of increasing human agency requires designing games with aligned incentive structures and better asymptotics for growth. I have a lot of thoughts queued up on how to do this, as there’s a lot to unpack here. I will publish a post about this soon.&lt;&#x2F;p&gt;
&lt;p&gt;On otoro.net, there’s this little volleyball game. Two little blobs, each with their own little neural network, play volleyball with one another, forever. This game directly inspired my recreation attempt in Scratch. The otoro.net website and its wonderful little blobs taught me that programming is an art, and learning is a craft. Even if discourse diverges, I can always return to the shared roots of making art and honing my craft.&lt;&#x2F;p&gt;
&lt;p&gt;With that in mind, here are things I’ve decided to do, as I navigate this tension:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;All my communication with others will be words that are spoken or typed myself. I will always be clear about the provenance of code and prose, and will never publish any AI-generated prose on this website. &lt;span class=aside&gt;The worst possible outcome is a world where two people are texting one another, with a serialization step to corporate-style email via LLM in between. It’s like talking to someone through a lawyer, but worse; at least lawyers are accountable. Just say what you want to say.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;I will make more art, and do so with others. I want to carve out some great corner of ideaspace, together. I long for a world where people are free to share valuable aesthetic contributions: to beautify the earth, the spaces we inhabit, and the relationships between the people that inhabit them. To choose to both be in a position of high agency and, when faced with many conflicting choices, to choose good.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Which roots do you find yourself returning to?&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;If you made it this far, you deserve another fun post. Here’s one I enjoyed a while back:&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jepsen.io&#x2F;analyses&#x2F;datomic-pro-1.0.7075&quot;&gt;Jepsen, Datomic Pro 1.0.7075&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Datalog is awesome; Datomic demonstrates the power of Clojure. I always enjoy reading a good Jepsen test, so I hope you enjoy this article!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Professional Home-Building Teams</title>
        <published>2026-02-03T00:00:00+00:00</published>
        <updated>2026-02-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-03/"/>
        <id>https://slightknack.dev/daily/2026-02-03/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-03/">&lt;p&gt;&lt;em&gt;A quick post tonight because I should already be in bed.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A lower-bound estimate for the number of calories the average adult American expends exercising a day is about ~150 calories per day (spent working out over the course of 30 minutes). According to the US Census Bureau, there are around 200 million Americans between the ages of 18 and 65. Multiplying these numbers, American adults expend 30,000,000,000 calories a day doing exercise. (Yes, that’s 30 billion calories).&lt;&#x2F;p&gt;
&lt;p&gt;I had a dream the other day where I was a part of a competitive home-building team. We competed against other teams to build identical houses to certain size and quality standards, as volunteers, instead of going to the gym. Hauling lumber, driving nails, raising roofs, rolling carpet, painting walls. It was a little surreal and very intense. &lt;span class=aside&gt;Are competitive home-building teams a real thing? Unclear.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, according to FRED, the median sales price for a house is currently $410,800. That amounts to, on average, a 3 bedroom house with 2 bathrooms, around 1,800 square feet. Based on data from the Bureau of Labor Statistics and the National Association of Home Builders, a home of this size would take around 3,600 labor-hours to complete, if the workers worked &lt;em&gt;very&lt;&#x2F;em&gt; slowly.&lt;&#x2F;p&gt;
&lt;p&gt;Building a home can can cost 200-600 calories per worker per hour, depending on the difficulty of the task; we can average that out to 300 calories per worker per hour. &lt;span class=aside&gt;Which also matches our earlier estimate for calories&#x2F;hour for exercise.&lt;&#x2F;span&gt; Multiplying these two numbers, the median home takes about 1,000,000 calories of human labor to build. (Yes, that’s 1 million calories.)&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;  30,000,000,000 (calories &#x2F; day)
&lt;&#x2F;span&gt;&lt;span&gt;÷      1,000,000 (calories &#x2F; home)
&lt;&#x2F;span&gt;&lt;span&gt;----------------
&lt;&#x2F;span&gt;&lt;span&gt;          30,000 (homes &#x2F; day)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If resources were no bottleneck, and everyone built homes instead of exercising, we could build 30,000 homes per day! According to the Census, there are ~140 million homes in the US. With some asterisks, at a rate of 30,000 homes per day, we could double the current US housing supply in about 13 years. &lt;span class=&quot;aside&quot;&gt;And as a side effect, everyone would be super ripped and very skilled at all sorts of crafts, of course.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Oh, in other news, I think I have an idea for a new type of gym…&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;I hope you enjoyed these fun facts!&lt;&#x2F;p&gt;
&lt;p&gt;Tangentially, I have this big list of articles and blog posts that I’ve read that I enjoyed. To share the love, I’m going to start including a random one in each of my daily posts, for your further enjoyment. If you’re here for the technical content, why not read this post next?&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Daily reading: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;brooker.co.za&#x2F;blog&#x2F;2023&#x2F;07&#x2F;28&#x2F;ds-testing.html&quot;&gt;Invariants, A Better Debugger?&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Habits and transition points</title>
        <published>2026-02-02T00:00:00+00:00</published>
        <updated>2026-02-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-02/"/>
        <id>https://slightknack.dev/daily/2026-02-02/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-02/">&lt;p&gt;Whenever you’re at a transition point in your life, &lt;span class=&quot;aside&quot;&gt;I wonder why I’m writing this!&lt;&#x2F;span&gt; moving to a new city, starting a new semester, changing jobs, making a life decision—you need to pay close attention to habits: both the ones you desire to keep and the ones you wish to lose.&lt;&#x2F;p&gt;
&lt;p&gt;Some habits are reinforced by your environment; others are encouraged by those around you. I like to think that habits don’t live inside you, but rather are prompted by signals subconciously perceived throughout the day: when you wake up, after you eat; when you leave, when you arrive; when you see someone, or remember them; when you see a note, remember an event, or check your schedule. &lt;span class=aside&gt;An example: a habit I have is that if I think of doing something nice or needed, like reaching out to an old friend, I have to do it the moment I think of it.&lt;&#x2F;span&gt; These little reminders get paired with little rituals, and the pairing of space, time, and action moves us smoothly through the day.&lt;&#x2F;p&gt;
&lt;p&gt;I’d say that it’s easier to lose good habits than it is to pick them up. &lt;span class=&quot;aside&quot;&gt;With the exception of habits that are naturally addictive… but I’d argue that an addictive habit is a &lt;em&gt;bad&lt;&#x2F;em&gt; habit. Good habits require effort.&lt;&#x2F;span&gt; Learning a habit requires work; making a concious effort to repeat an action consistently until it becomes natural. Losing habits requires… doing nothing. Like a bustling city, the cost of any habit is constant maintenence.&lt;&#x2F;p&gt;
&lt;p&gt;After a transition point, when with new people or when in a new place, the signals you’ve been relying on to carry out a habit are missing. You need to make the concious choice to keep up the habit.&lt;&#x2F;p&gt;
&lt;p&gt;In coming back to school and starting a new semester, there are a few things that I would like to start up again. &lt;span class=aside&gt;Like my laundry.&lt;&#x2F;span&gt; I also hope to maintain the habit of writing daily, but that’s quite a bit harder. So if I miss a day, let me know :)&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Oh! today I also wrote a new homepage for this website. The last one hadn’t been updated since high school. You can take a look at it &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;isaac.sh&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Classes start tomorrow</title>
        <published>2026-02-01T00:00:00+00:00</published>
        <updated>2026-02-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-02-01/"/>
        <id>https://slightknack.dev/daily/2026-02-01/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-02-01/">&lt;p&gt;Today is Sunday, the day of rest. My day today was very good. (My goodness it’s so nice to be back home in Cambridge, MA.) I was debating whether or not to post anything today, but I figured that as I’ve been going strong for 2 days, I might as well keep it up.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;N.B.&lt;&#x2F;em&gt; If you read this blog over RSS, and you’re seeing all these posts and are worried it might be a little much, please let me know and I’ll move these daily entries to a separate feed. (You can get in touch with me via the email on my &lt;a href=&quot;&#x2F;about&quot;&gt;about&lt;&#x2F;a&gt; page.)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Anyway, classes start tomorrow. In typical MIT fashion I registered for 12 classes and now have to trim down to a subset of 4 or 5. I’m most excited about &lt;em&gt;Inference and Information&lt;&#x2F;em&gt;, though. (The smartest guy I know told me it’s the hardest class he’s taken. So I might be cooked! We’ll see.)&lt;&#x2F;p&gt;
&lt;p&gt;Also it’s absolutely frigid! I forgot that the temperature could get this low! (While I’m frozen here, enjoy this view from the flight back.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;content&#x2F;IDG_20260131_164451_716.jpg&quot; alt=&quot;Where the city meets the water at night, bridges and roads form webs and strings of beads of light&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Chisel</title>
        <published>2026-01-31T00:00:00+00:00</published>
        <updated>2026-01-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-01-31/"/>
        <id>https://slightknack.dev/daily/2026-01-31/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-01-31/">&lt;p&gt;I’ve been a fan of Conflict-Free replicated datatypes for a while. I wrote an &lt;a href=&quot;&#x2F;blog&#x2F;backing-crdt-store&quot;&gt;old blog post about implementing them back in 2021&lt;&#x2F;a&gt;, &lt;span class=&quot;aside&quot;&gt;Wow, almost 5 years ago.&lt;&#x2F;span&gt; and I’ve been turning them over in my mind ever since.&lt;&#x2F;p&gt;
&lt;p&gt;If you’re not familiar with what a CRDT is, I’ll provide this explanation I wrote, taken from a project I am currently working on: &lt;span class=&quot;aside&quot;&gt;The project is called Together, as a part of Veritable &#x2F; Solidarity &#x2F; Home &#x2F; Isocore.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;h1 style=&quot;margin-top: 20pt&quot;&gt;Background on CRDTs&lt;&#x2F;h1&gt;
&lt;p&gt;A CRDT is a datatype with a single operator, merge. Merge is an operation that takes a pair of values and produces a new value, merging the two. This operator has some special properties. Specifically, only requirement on merge is that, for all types, it is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Commutative, meaning &lt;code&gt;merge(A, B)&lt;&#x2F;code&gt; is the same as &lt;code&gt;merge(B, A)&lt;&#x2F;code&gt;; the order of operations does not matter.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Associative, meaning &lt;code&gt;merge(A, merge(B, C))&lt;&#x2F;code&gt; is the same as &lt;code&gt;merge(merge(A, B), C)&lt;&#x2F;code&gt;; the grouping of operations does not matter.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Idempotent, meaning &lt;code&gt;merge(A, merge(A, B))&lt;&#x2F;code&gt; is the same as &lt;code&gt;merge(A, B)&lt;&#x2F;code&gt;; applying the same operation multiple times does not change the result.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A simple example of a CRDT is a max counter over the integers. The merge operator simply takes whichever argument is greater. We can define it in Rust like so:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;MaxCounter &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;merge&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;: MaxCounter, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;: MaxCounter) -&amp;gt; MaxCounter {
&lt;&#x2F;span&gt;&lt;span&gt;  MaxCounter { value: a.value.max(b.value) }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Imagine we have three integer values such that &lt;code&gt;A &amp;gt; B &amp;gt; C&lt;&#x2F;code&gt;. Then, it is trivial to show that max counter is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Commutative, as
&lt;code&gt;max(A, B) = A = max(B, A)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Associative, as
&lt;code&gt;max(A, max(B, C)) = A = max(max(A, B), C)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Idempotent, as
&lt;code&gt;max(A, max(A, B)) = A = max(A, B)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Therefore max over the integers forms a CRDT.&lt;&#x2F;p&gt;
&lt;p&gt;There are lots of other cool CRDTs, like replicated growth arrays, for more complicated structures of data. The core idea of CRDTs is that if the merge operator forms a (join, semi-) lattice, we can take any set of changes and merge them in any order and eventually arrive at the same result. This has very nice properties for collaboration, of course.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;I am not a pioneer in the space, so if you’d like a better explanation, search &lt;code&gt;site:lobste.rs crdt&lt;&#x2F;code&gt; or something.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-isocore-yap&quot;&gt;The Isocore Yap&lt;&#x2F;h1&gt;
&lt;p&gt;I am currently working on &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;isocore&quot;&gt;Isocore&lt;&#x2F;a&gt;, which is a BEAM-like runtime for WebAssembly (Wasm) components. &lt;span class=&quot;aside&quot;&gt;Think of each component like an Erlang module.&lt;&#x2F;span&gt; You can write distributed applications that run across multiple computers: we carve Wasm components apart at the interface boundary (WIT), automatically intercepting and converting arbitrary interface calls into RPC calls. Under the hood we handle all the routing, orchestration, supervision, data replication, authorization, etc..&lt;&#x2F;p&gt;
&lt;p&gt;Isocore is designed to be pretty small, with an interface you could bundle as a Wasm component itself and send to a browser. So a server running an Isocore node could serve a copy of Isocore to a browser, which connects back into the isocore cluster using some protocol like websockets or webtransport or webRTC. Then the web node could fetch applications from the parent node, or call remote functions (e.g. persist a file, write to a database, perform a transaction) by binding that application to interface implementations running on the server node (or cluster, broadly).&lt;&#x2F;p&gt;
&lt;p&gt;This is something of a personal art project I have been working on for the past, say, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Tloru&#x2F;Veritable&quot;&gt;7 years or so&lt;&#x2F;a&gt;. &lt;span class=&quot;aside&quot;&gt;The project has grown and changed a lot over the years, but the core idea is to create a better version of the web, something along the lines of a universal application runtime that also serves as a desktop environment &#x2F; operating system. I’m trying to do it from scratch as much as possible, because it’s for my own learning and enjoyment.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Isocore is designed to be distributed, or federated. Because authorization is capability-based, everything is sandboxed, we use strong cryptographic primitives, etc. I want it to be common and safe to e.g. pull Wasm component applications hosted by nodes on different clusters, or even have different clusters run by different people collaborate on running the same application. Like, if I run an instance of Isocore at &lt;code&gt;home.isaac.sh&lt;&#x2F;code&gt;, and you run one at &lt;code&gt;lab.unnamed.website&lt;&#x2F;code&gt;, and there’s a common chat application named &lt;code&gt;harmony-chat&lt;&#x2F;code&gt; or something that we both run, I should be able to chat with you by pointing my cluster at yours. This requires Isocore nodes be simple and provide general compute&#x2F;storage&#x2F;transport services, where permission to use these resources is strictly governed by whoever runs the node&#x2F;cluster.&lt;&#x2F;p&gt;
&lt;p&gt;If we’re working in a distributed context, to build this vision, we first need to polyfill some distributed primitives that are missing. The biggest things that any distributed system needs, in this sense, are:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Simple protocols for cryptography and authorization.&lt;&#x2F;li&gt;
&lt;li&gt;Mass immutable blob store, a la bittorrent; with a mutable filesystem-like layer on top.&lt;&#x2F;li&gt;
&lt;li&gt;Encrypted streaming peer-to-peer data replication, a la hypercore.&lt;&#x2F;li&gt;
&lt;li&gt;Single-binary multi-node capability-based sandboxed application runtimes. &lt;span class=&quot;aside&quot;&gt;Which is what Isocore is.&lt;&#x2F;span&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Authenticated CRDT libraries for collaboration, sync, and versioning. &lt;span class=&quot;aside&quot;&gt;Which is what Together is.&lt;&#x2F;span&gt;&lt;&#x2F;li&gt;
&lt;li&gt;A general UI layer (whether web or native) for building human-friendly non-shoebox applications.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;So far I’ve implemented most of point 1 (standard primitives using ed15519-dalek, XChaCha20-Poly1305, etc.), written multiple types of point 2 in different contexts (in-memory, on-disk, to-cloud), implemented point 3 a handful of times. &lt;span class=&quot;aside&quot;&gt;Most recently at my last job, on top of QUIC.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I’m working on 4, 5, and 6. For point 6, I’m planning to start by using good old html, css, and javascript + wasm. &lt;span class=&quot;aside&quot;&gt;It would be cool to wire up a cross-platform UI library from scratch, but I’m aware enough to understand how big of a task that is.&lt;&#x2F;span&gt; I’m a firm believer in static site + authenticated RPC calls, and although I have not used it, I am partial to &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;&quot;&gt;htmx&lt;&#x2F;a&gt;. I dislike React, but &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;acko.net&#x2F;blog&#x2F;live-2015&#x2F;&quot;&gt;Live&lt;&#x2F;a&gt; by Steven Wittens is cool. Anyway.&lt;&#x2F;p&gt;
&lt;p&gt;Point 4, Isocore, will be the subject of another blog post. This is a long preamble to say that today I will be talking about CRDTs, and a library which I’m writing called &lt;strong&gt;Together&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I started Together back in 2018, but then abandoned it for a season; here is the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;together&quot;&gt;current repo&lt;&#x2F;a&gt;. &lt;span class=&quot;aside&quot;&gt;(Un)fortunately, I registered the crate name &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;together&#x2F;0.1.0&quot;&gt;&lt;code&gt;together&lt;&#x2F;code&gt; on crates.io&lt;&#x2F;a&gt;, but never ended up publishing any of the code I wrote.&lt;&#x2F;span&gt; Now that I’m picking up development again, I hope to make good use of this namespace going forward.&lt;&#x2F;p&gt;
&lt;p&gt;Which brings us to today’s adventure. &lt;span class=&quot;aside&quot;&gt;Sorry to bury the lede.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;together-or-something-is-profoundly-wrong-with-the-state-of-ai-programming-tools-right-now&quot;&gt;Together, or: Something is profoundly wrong with the state of AI programming tools right now&lt;&#x2F;h1&gt;
&lt;p&gt;So yesterday, I was working on Together, my CRDT library. &lt;span class=&quot;aside&quot;&gt;Which is why I &lt;a href=&quot;&#x2F;daily&#x2F;2026-01-30&#x2F;&quot;&gt;brought up CRDTs when chatting with Mr. Dean yesterday&lt;&#x2F;a&gt;.&lt;&#x2F;span&gt; By hand, I wrote a very simple replicated growth array (RGA), with explicit trees and &lt;code&gt;O(n)&lt;&#x2F;code&gt; indexing and everything. Most importantly, I made sure my implementation was correct. I wrote a handful of unit tests for the edge cases.&lt;&#x2F;p&gt;
&lt;p&gt;Then I ran my RGA implementation, and it was slow. I know that &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;josephg.com&#x2F;blog&#x2F;crdts-go-brrr&#x2F;&quot;&gt;CRDTs don’t have to be slow&lt;&#x2F;a&gt;. &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;josephg&#x2F;diamond-types&quot;&gt;Diamond-types&lt;&#x2F;a&gt; is a state-of-the-art library with performant CRDTs. So, off the dome, I wrote down a long list of possible optimizations, and got to work.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to first resolve linear indexing. So I started writing a skip-list by hand. About halfway through, I decided to try something new.&lt;&#x2F;p&gt;
&lt;p&gt;So I asked &lt;code&gt;claude-opus-4-5&lt;&#x2F;code&gt; (which I will refer to as “the chisel”) to read the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tigerbeetle&#x2F;tigerbeetle&#x2F;blob&#x2F;main&#x2F;docs&#x2F;TIGER_STYLE.md&quot;&gt;TigerStyle document on GitHub&lt;&#x2F;a&gt;, and a huge list of blog posts and style tips I had curated into this big ol’ document named &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;together&#x2F;blob&#x2F;master&#x2F;PROCESS.md&quot;&gt;Process&lt;&#x2F;a&gt;. I then asked the chisel to generate an exhaustive set of unit tests for my existing slow-but-correct reference implementation. I asked the chisel to additionally generate a number of property-based tests, for all invariants, and benchmarks comparing my implementation to diamond-types on a number of &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;josephg&#x2F;editing-traces&quot;&gt;standard benchmarks from &lt;code&gt;josephg&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here is how diamond-types performs on these standard editing traces:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Trace&lt;&#x2F;th&gt;&lt;th&gt;Ops&lt;&#x2F;th&gt;&lt;th&gt;Diamond (ms)&lt;&#x2F;th&gt;&lt;th&gt;Description&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;sveltecomponent&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;19,749&lt;&#x2F;td&gt;&lt;td&gt;1.70&lt;&#x2F;td&gt;&lt;td&gt;Editing a Svelte component file&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;rustcode&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;40,173&lt;&#x2F;td&gt;&lt;td&gt;4.30&lt;&#x2F;td&gt;&lt;td&gt;Editing Rust source code&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;seph-blog1&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;137,993&lt;&#x2F;td&gt;&lt;td&gt;9.10&lt;&#x2F;td&gt;&lt;td&gt;Writing a blog post&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;automerge-paper&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;259,778&lt;&#x2F;td&gt;&lt;td&gt;15.4&lt;&#x2F;td&gt;&lt;td&gt;Writing the Automerge academic paper&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Which is pretty fast! &lt;span class=&quot;aside&quot;&gt;I knew that we could at least achieve this speed in principle.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I needed to sleep, so I left my computer running overnight and asked the chisel to, essentially:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Benchmark the current implementation.&lt;&#x2F;li&gt;
&lt;li&gt;Research the next best optimization.&lt;&#x2F;li&gt;
&lt;li&gt;Implement it, ensuring all tests still passed and the API stayed the same.&lt;&#x2F;li&gt;
&lt;li&gt;Benchmark again and commit if the code ran faster.&lt;&#x2F;li&gt;
&lt;li&gt;Reflect and add to the list of future optimization ideas.&lt;&#x2F;li&gt;
&lt;li&gt;Repeat until together was faster than diamond-types.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For context, here is how slow my reference implementation was:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Trace&lt;&#x2F;th&gt;&lt;th&gt;Diamond (ms)&lt;&#x2F;th&gt;&lt;th&gt;Together (ms)&lt;&#x2F;th&gt;&lt;th&gt;vs Diamond (times slower)&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;sveltecomponent&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;1.70&lt;&#x2F;td&gt;&lt;td&gt;465&lt;&#x2F;td&gt;&lt;td&gt;274×&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;rustcode&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;4.30&lt;&#x2F;td&gt;&lt;td&gt;~1,500&lt;&#x2F;td&gt;&lt;td&gt;~350×&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;seph-blog1&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;9.10&lt;&#x2F;td&gt;&lt;td&gt;~6,000&lt;&#x2F;td&gt;&lt;td&gt;~660×&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;automerge-paper&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;15.4&lt;&#x2F;td&gt;&lt;td&gt;~12,000&lt;&#x2F;td&gt;&lt;td&gt;~780×&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;When I woke up in the morning, I was greeted with this table: &lt;span class=&quot;aside&quot;&gt;Which I had asked the chisel to prepare upon completion.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;No.&lt;&#x2F;th&gt;&lt;th&gt;Optimization&lt;&#x2F;th&gt;&lt;th&gt;Step Speedup&lt;&#x2F;th&gt;&lt;th&gt;vs Diamond (1.0× = parity)&lt;&#x2F;th&gt;&lt;th&gt;Result&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;Unoptimized (naive &lt;code&gt;Vec&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;—&lt;&#x2F;td&gt;&lt;td&gt;274×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;Remove &lt;code&gt;HashMap&lt;&#x2F;code&gt; index&lt;&#x2F;td&gt;&lt;td&gt;8.8×&lt;&#x2F;td&gt;&lt;td&gt;31×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;Chunked weighted list&lt;&#x2F;td&gt;&lt;td&gt;77×&lt;&#x2F;td&gt;&lt;td&gt;4.5×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;Span coalescing&lt;&#x2F;td&gt;&lt;td&gt;1.9×&lt;&#x2F;td&gt;&lt;td&gt;2.4×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;&#x2F;td&gt;&lt;td&gt;Combined origin&#x2F;insert lookup&lt;&#x2F;td&gt;&lt;td&gt;1.2×&lt;&#x2F;td&gt;&lt;td&gt;2.0×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;td&gt;Compact &lt;code&gt;Span&lt;&#x2F;code&gt; (112 to 24 bytes)&lt;&#x2F;td&gt;&lt;td&gt;1.3×&lt;&#x2F;td&gt;&lt;td&gt;1.5×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;6&lt;&#x2F;td&gt;&lt;td&gt;Fenwick tree for chunk weights&lt;&#x2F;td&gt;&lt;td&gt;1.6×&lt;&#x2F;td&gt;&lt;td&gt;1.5×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;td&gt;Binary search over chunks&lt;&#x2F;td&gt;&lt;td&gt;0.9×&lt;&#x2F;td&gt;&lt;td&gt;—&lt;&#x2F;td&gt;&lt;td&gt;reverted&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;8&lt;&#x2F;td&gt;&lt;td&gt;Hybrid Fenwick&#x2F;linear scan&lt;&#x2F;td&gt;&lt;td&gt;—&lt;&#x2F;td&gt;&lt;td&gt;1.5×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;9&lt;&#x2F;td&gt;&lt;td&gt;Cursor caching&lt;&#x2F;td&gt;&lt;td&gt;1.2×&lt;&#x2F;td&gt;&lt;td&gt;1.3×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;10&lt;&#x2F;td&gt;&lt;td&gt;Skip list for spans&lt;&#x2F;td&gt;&lt;td&gt;0.0005×&lt;&#x2F;td&gt;&lt;td&gt;—&lt;&#x2F;td&gt;&lt;td&gt;reverted&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;11&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;RgaBuf&lt;&#x2F;code&gt; (buffered writes)&lt;&#x2F;td&gt;&lt;td&gt;1.30×&lt;&#x2F;td&gt;&lt;td&gt;1.0×&lt;&#x2F;td&gt;&lt;td&gt;parity&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;12&lt;&#x2F;td&gt;&lt;td&gt;Backspace optimization&lt;&#x2F;td&gt;&lt;td&gt;1.10×&lt;&#x2F;td&gt;&lt;td&gt;1.0×&lt;&#x2F;td&gt;&lt;td&gt;parity&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;13&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;SmallVec&lt;&#x2F;code&gt; for pending content&lt;&#x2F;td&gt;&lt;td&gt;1.05×&lt;&#x2F;td&gt;&lt;td&gt;1.0×&lt;&#x2F;td&gt;&lt;td&gt;parity&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;14&lt;&#x2F;td&gt;&lt;td&gt;Inline hints + &lt;code&gt;debug_assert&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;1.05×&lt;&#x2F;td&gt;&lt;td&gt;1.0×&lt;&#x2F;td&gt;&lt;td&gt;parity&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;15&lt;&#x2F;td&gt;&lt;td&gt;Chunk location caching&lt;&#x2F;td&gt;&lt;td&gt;1.05×&lt;&#x2F;td&gt;&lt;td&gt;1.0×&lt;&#x2F;td&gt;&lt;td&gt;parity&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;16&lt;&#x2F;td&gt;&lt;td&gt;B-Tree for spans&lt;&#x2F;td&gt;&lt;td&gt;1.30×&lt;&#x2F;td&gt;&lt;td&gt;1.0×&lt;&#x2F;td&gt;&lt;td&gt;parity&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;17&lt;&#x2F;td&gt;&lt;td&gt;Smaller B-tree leaves&lt;&#x2F;td&gt;&lt;td&gt;0.9×&lt;&#x2F;td&gt;&lt;td&gt;—&lt;&#x2F;td&gt;&lt;td&gt;reverted&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;18&lt;&#x2F;td&gt;&lt;td&gt;Larger B-tree leaves&lt;&#x2F;td&gt;&lt;td&gt;0.95×&lt;&#x2F;td&gt;&lt;td&gt;—&lt;&#x2F;td&gt;&lt;td&gt;reverted&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;19&lt;&#x2F;td&gt;&lt;td&gt;Simplified cursor cache&lt;&#x2F;td&gt;&lt;td&gt;0.95×&lt;&#x2F;td&gt;&lt;td&gt;—&lt;&#x2F;td&gt;&lt;td&gt;reverted&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;20&lt;&#x2F;td&gt;&lt;td&gt;Delete buffering&lt;&#x2F;td&gt;&lt;td&gt;2.8×&lt;&#x2F;td&gt;&lt;td&gt;0.56×&lt;&#x2F;td&gt;&lt;td&gt;faster&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;21&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;FxHashMap&lt;&#x2F;code&gt; for &lt;code&gt;UserTable&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;1.1×&lt;&#x2F;td&gt;&lt;td&gt;0.50×&lt;&#x2F;td&gt;&lt;td&gt;faster&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;22&lt;&#x2F;td&gt;&lt;td&gt;Fix YATA&#x2F;FugueMax bugs&lt;&#x2F;td&gt;&lt;td&gt;0.15×&lt;&#x2F;td&gt;&lt;td&gt;6.7×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;23&lt;&#x2F;td&gt;&lt;td&gt;ID lookup index for merge&lt;&#x2F;td&gt;&lt;td&gt;1.0×&lt;&#x2F;td&gt;&lt;td&gt;6.7×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;24&lt;&#x2F;td&gt;&lt;td&gt;Origin position hint&lt;&#x2F;td&gt;&lt;td&gt;6.0×&lt;&#x2F;td&gt;&lt;td&gt;1.1×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;25&lt;&#x2F;td&gt;&lt;td&gt;Fast path for YATA scan&lt;&#x2F;td&gt;&lt;td&gt;1.05×&lt;&#x2F;td&gt;&lt;td&gt;1.1×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;26&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;SmallVec&lt;&#x2F;code&gt; for subtree tracking&lt;&#x2F;td&gt;&lt;td&gt;1.02×&lt;&#x2F;td&gt;&lt;td&gt;1.1×&lt;&#x2F;td&gt;&lt;td&gt;slower&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;27&lt;&#x2F;td&gt;&lt;td&gt;LTO + single codegen unit&lt;&#x2F;td&gt;&lt;td&gt;1.03×&lt;&#x2F;td&gt;&lt;td&gt;1.0×&lt;&#x2F;td&gt;&lt;td&gt;parity&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;28&lt;&#x2F;td&gt;&lt;td&gt;Origin index for O(k) sibling lookup&lt;&#x2F;td&gt;&lt;td&gt;1.25×&lt;&#x2F;td&gt;&lt;td&gt;0.79×&lt;&#x2F;td&gt;&lt;td&gt;faster&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;29&lt;&#x2F;td&gt;&lt;td&gt;Stable origin IDs (user_idx, seq)&lt;&#x2F;td&gt;&lt;td&gt;1.05×&lt;&#x2F;td&gt;&lt;td&gt;0.79×&lt;&#x2F;td&gt;&lt;td&gt;faster&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;30&lt;&#x2F;td&gt;&lt;td&gt;Right origin tracking for merge&lt;&#x2F;td&gt;&lt;td&gt;1.02×&lt;&#x2F;td&gt;&lt;td&gt;0.79×&lt;&#x2F;td&gt;&lt;td&gt;faster&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;span class=&quot;aside&quot;&gt;Note: the other benchmarks would take a long time to run so I asked the chisel to run against only &lt;code&gt;sveltecomponent&lt;&#x2F;code&gt; until the other benchmarks were fast enough to run.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Is that not crazy!? Over the course of a night, the chisel implemented 30 optimizations and sped up the code by a factor of over 300×, while preserving all interfaces and maintaining correct semantics.&lt;&#x2F;p&gt;
&lt;p&gt;Benchmarking the final implementation against diamond-types:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Trace&lt;&#x2F;th&gt;&lt;th&gt;Diamond (ms)&lt;&#x2F;th&gt;&lt;th&gt;Together (ms)&lt;&#x2F;th&gt;&lt;th&gt;Speedup&lt;&#x2F;th&gt;&lt;th&gt;Diamond (ns&#x2F;op)&lt;&#x2F;th&gt;&lt;th&gt;Together (ns&#x2F;op)&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;sveltecomponent&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;1.48&lt;&#x2F;td&gt;&lt;td&gt;1.17&lt;&#x2F;td&gt;&lt;td&gt;1.26×&lt;&#x2F;td&gt;&lt;td&gt;75&lt;&#x2F;td&gt;&lt;td&gt;59&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;rustcode&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;3.64&lt;&#x2F;td&gt;&lt;td&gt;2.96&lt;&#x2F;td&gt;&lt;td&gt;1.23×&lt;&#x2F;td&gt;&lt;td&gt;91&lt;&#x2F;td&gt;&lt;td&gt;74&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;seph-blog1&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;8.57&lt;&#x2F;td&gt;&lt;td&gt;4.87&lt;&#x2F;td&gt;&lt;td&gt;1.76×&lt;&#x2F;td&gt;&lt;td&gt;62&lt;&#x2F;td&gt;&lt;td&gt;35&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;automerge-paper&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;14.33&lt;&#x2F;td&gt;&lt;td&gt;4.41&lt;&#x2F;td&gt;&lt;td&gt;3.25×&lt;&#x2F;td&gt;&lt;td&gt;55&lt;&#x2F;td&gt;&lt;td&gt;17&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Which is impressive, now beating diamond-types on all four traces.&lt;&#x2F;p&gt;
&lt;p&gt;Now, to be completely clear:&lt;&#x2F;p&gt;
&lt;p&gt;I take no credit for any of this. A lot of these optimizations were pioneered by &lt;code&gt;josephg&lt;&#x2F;code&gt; and other friends of mine who work on CRDTs. On a greenfield problem, there would not have been a well-worn path to follow. The chisel launders good ideas, which is as incredible as it is terrifying.&lt;&#x2F;p&gt;
&lt;p&gt;After reading through the code, Together is faster due to a couple of unique design decisions. First, I specifically designed Together to require much smaller &lt;code&gt;Span&lt;&#x2F;code&gt; types, &lt;span class=&quot;aside&quot;&gt;Together is 24 bytes, diamond-types is 40.&lt;&#x2F;span&gt; so more can fit in a cache line. Together also buffers inserts and deletes, as most edits in the test dataset are local continuous insertions and deletions while typing. These two optimizations allow Together to light up a core and rip through sequences of merge operations.&lt;&#x2F;p&gt;
&lt;p&gt;It’s completely possible that on real-world data, or when used for other applications, Together is slower. Diamond-types is an incredible, more general library; Together, while having extensive property-based tests, is not. Optimizations have tradeoffs, and the design decision of buffering edits, at a design level, may be at odds with the goals of diamond-types, for example.&lt;&#x2F;p&gt;
&lt;p&gt;Here is what is profoundly wrong: I have no right to have used the chisel to get this far in one night. This experience was a strange and vertigo-inducing way to write software: I wrote out the high-level design, I wrote the types and the API, I brainstormed all the little tricks a real-world implementation might use. Then I went to bed.&lt;&#x2F;p&gt;
&lt;p&gt;I guess my specification was good enough to will a competitive implementation into existence? I’m scared to see how far I could push this; after all, there’s still a long list of optimization ideas left.&lt;&#x2F;p&gt;
&lt;p&gt;We’ll see.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; I benchmarked against a handful of major Rust CRDT libraries: &lt;span class=&quot;aside&quot;&gt;Mostly Rust, with one JS library.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Library&lt;&#x2F;th&gt;&lt;th&gt;&lt;code&gt;sveltecomponent&lt;&#x2F;code&gt; (ms)&lt;&#x2F;th&gt;&lt;th&gt;&lt;code&gt;rustcode&lt;&#x2F;code&gt; (ms)&lt;&#x2F;th&gt;&lt;th&gt;&lt;code&gt;seph-blog1&lt;&#x2F;code&gt; (ms)&lt;&#x2F;th&gt;&lt;th&gt;&lt;code&gt;automerge-paper&lt;&#x2F;code&gt; (ms)&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;together&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;1.17&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;2.96&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;4.87&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;4.41&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;diamond-types&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;1.48&lt;&#x2F;td&gt;&lt;td&gt;3.64&lt;&#x2F;td&gt;&lt;td&gt;8.57&lt;&#x2F;td&gt;&lt;td&gt;14.33&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;cola-crdt&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;2.48&lt;&#x2F;td&gt;&lt;td&gt;21.47&lt;&#x2F;td&gt;&lt;td&gt;38.96&lt;&#x2F;td&gt;&lt;td&gt;142.98&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;json-joy&lt;&#x2F;code&gt; (JS)&lt;&#x2F;td&gt;&lt;td&gt;7.64&lt;&#x2F;td&gt;&lt;td&gt;25.13&lt;&#x2F;td&gt;&lt;td&gt;53.49&lt;&#x2F;td&gt;&lt;td&gt;99.19&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;loro&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;15.16&lt;&#x2F;td&gt;&lt;td&gt;36.16&lt;&#x2F;td&gt;&lt;td&gt;77.20&lt;&#x2F;td&gt;&lt;td&gt;144.84&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;automerge&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;165.20&lt;&#x2F;td&gt;&lt;td&gt;1180.60&lt;&#x2F;td&gt;&lt;td&gt;431.83&lt;&#x2F;td&gt;&lt;td&gt;303.34&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;yrs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;359.38&lt;&#x2F;td&gt;&lt;td&gt;1182.30&lt;&#x2F;td&gt;&lt;td&gt;5563.46&lt;&#x2F;td&gt;&lt;td&gt;6520.55&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;span class=&quot;aside&quot;&gt;To be fair, most of these libraries are general-purpose and support many different CRDT structures, not just RGAs. But I think with enough layers of RGAs you can basically represent anything so ymmv.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;div class=boxed&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;&#x2F;strong&gt; microbenchmarks are hard to get right, I’m running on a 2023 M3 Pro, there are lots of other metrics I didn’t measure, etc.
&lt;strong&gt;Prescription:&lt;&#x2F;strong&gt; take with cubes of salt.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;&lt;span class=&quot;aside&quot;&gt;I also stubbed out and benchmarked the Zed &lt;code&gt;text&lt;&#x2F;code&gt; crate, just out of curiosity, but it was so slow I’m sure I must have done something incorrectly. It didn’t seem fair to include it as a comparison.&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-chisel-works-while-i-blog&quot;&gt;The chisel works while I blog&lt;&#x2F;h1&gt;
&lt;p&gt;While I was writing this up I wanted to put the chisel to work. I designed a nice high-level API for reading slices of a CRDT document to &lt;code&gt;Strings&lt;&#x2F;code&gt; and keeping track of anchors in a document. This means that if your e.g. cursor is highlighting something in a document, and someone else edits inside of the highlight, the highlight expands as the anchors move with the text. For example, using &lt;code&gt;]&lt;&#x2F;code&gt; and &lt;code&gt;[&lt;&#x2F;code&gt; to represent highlight anchors:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;The cat sat on ]the mat[.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;The cat sat on ]the fluffy mat[.
&lt;&#x2F;span&gt;&lt;span&gt;                    ++++++
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I also want to be able to rewind to any prior version in the document. So I brainstormed three ways to do this, to summarize:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Add logical timestamps and filter newer edits while traversing the CRDT.&lt;&#x2F;li&gt;
&lt;li&gt;Use a persistent B-Tree so old versions still exist and can be read&#x2F;forked.&lt;&#x2F;li&gt;
&lt;li&gt;Store periodic checkpoints to avoid having to apply all edits from the beginning when loading an old version.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I wrote up a spec and some tests and I asked the chisel to get to work: implement each approach in a new branch, benchmark at the end to pick the best approach. And the chisel just… did the work.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;on-the-future-of-software-development&quot;&gt;On the future of software development.&lt;&#x2F;h1&gt;
&lt;p&gt;I have two unpublished essays, each about 10k words long. The first is called “Gradually Casting Systems in Stone”. It is about the spectrum from sketching ideas in design documents to formally verifying core systems. We need tools that help us gradually cast systems in stone, tools that move ideas from abstract to concrete, concrete to correct, correct to fast. A codebase can be seen as a living system, where calcified components that have been cast in stone are strung together by a growing layer of new ideas that percolate and harden themselves. I genuinely believe that as tools improve I have enough knowledge of the systems that underpin the web to not only rewrite it from scratch, but to write a better one, that avoids many design mistakes of the original, and is solid, secure, and cast in stone.&lt;&#x2F;p&gt;
&lt;p&gt;The second essay is called “Programming Beyond Text”. It outlines a vision for what programming will become over the next few years. &lt;span class=&quot;aside&quot;&gt;It was written before language models, so surprisingly, a lot of it is about design from a pure UX perspective, and not how to cram the round peg of technology we have into the square hole of what is needed.&lt;&#x2F;span&gt; I will write more about it shortly, and perhaps even release it, but the idea is to make feedback loops tighter, capture the magic of smalltalk-like image systems while keeping them legible, and show how edits to code change data and vice versa.&lt;&#x2F;p&gt;
&lt;p&gt;Say you’re writing a react component in your code editor of choice. Imagine there was an interactive, in-line preview of the component right below the code for it. Inline with the code itself would be the current state of each variable, just like how inferred types are annotated. You would be able to edit the code or the state, and watch the inline preview update instantly. Imagine this preview were bidirectional: you could modify the component, and the code would update. You could pull up a Figma-like design window, and use that to tweak the colors, shapes, and sizes of things.&lt;&#x2F;p&gt;
&lt;p&gt;With a chisel, an editor designed for programming beyond text would become infinitely malleable. &lt;span class=&quot;aside&quot;&gt;I feel like this idea is inevitable and it will exist at some point. While at Zed, I tried to design the extension system for the editor with this in mind. I hope that one day the editor exposes its UI primitives in a capability-safe way, so little composable tools can be used to turn Zed from a text editor into a general-purpose application platform for development work.&lt;&#x2F;span&gt; Imagine generating tools on demand to visualize and edit code in formats other that text. Using chisels to generate tools that modify code is different than vibecoding. It’s the difference between “zero-control creative-process-in-a-box text-to-image generation” and “photoshop with great tools that feel like magic”: one enables creative control, the other takes it from you. Whether tools are written by hand or composed on the fly, these lenses are concrete reifications that deeply integrate with the language runtime and the editor. Wouldn’t that be nice?&lt;&#x2F;p&gt;
&lt;p&gt;I’m excited. Today, most new applications are “shoebox”, or siloed, because system-level primitives that encourage users to manage their own data do not yet exist. (Does anybody use an OS with practical filesystem branching versioning?) If you want to break that trend, write something like Apeture, you must expend a lot of work and enforce a holistic engineering discipline to build one of these “true professional applications”. I would love for this to be easier. The tools will get better. I can’t wait to program beyond text and work to gradually carve beautiful systems in stone.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>I wish I were a better conversationalist</title>
        <published>2026-01-30T00:00:00+00:00</published>
        <updated>2026-01-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-01-30/"/>
        <id>https://slightknack.dev/daily/2026-01-30/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-01-30/">&lt;div class=boxed&gt;
&lt;p&gt;“Well okay I’m so glad you got to call him, who was he? The Dean of AI at Google?”&lt;&#x2F;p&gt;
&lt;p&gt;“Haha his name is Dean. Jeff Dean. But yes.”&lt;&#x2F;p&gt;
&lt;p&gt;— Conversation with my girlfriend after the call.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;I had the opportunity to meet Jeff Dean today, on &lt;em&gt;very&lt;&#x2F;em&gt; short notice. I am at a loss for words. He is an awesome guy.&lt;&#x2F;p&gt;
&lt;p&gt;I never was into pop stars or celebrities as a kid. (I know, crazy.) I only realize now that, although I mostly grew up writing code instead of following the drip of gossip, I also have my idols.&lt;&#x2F;p&gt;
&lt;p&gt;I have deep reverence for the great names in computer science: Knuth, Djikstra, Kay; Dean, Norvig, Cox, to name a few. I didn’t realize that I had built up, in my mind, something of an altar to these people. If computation was God’s language, these were his prophets. And I am a God-fearing man.&lt;&#x2F;p&gt;
&lt;p&gt;As a kid, I memorized every Jeff Dean fact. I would read about BigQuery and MapReduce while on the school bus each morning. (Even today, I often use Google Search, Jeff’s Noogler project.) Jeff Dean is a much larger part of my headcanon than I am a part of his. I never thought I’d meet him.&lt;&#x2F;p&gt;
&lt;p&gt;This morning, I was talking to a friend, and he asked if there was anyone I wanted to meet. I mentioned that it was a dream of mine to one day meet Jeff Dean, not thinking anything of it. Just now, my friend called me, out of the blue, and asked if I would like to speak to Jeff Dean within the next hour.&lt;&#x2F;p&gt;
&lt;p&gt;Is he crazy? Of course I would! How could I ever say no!?&lt;&#x2F;p&gt;
&lt;p&gt;So a handful minutes later, I’m finally sitting there, on a Google Meet call, face to face with Mr. Dean. I was at a loss for words.&lt;&#x2F;p&gt;
&lt;p&gt;How to situate you, dear reader? I can only try: Here was this man, who I felt as though I knew so much about, who I believed knew so little about me, waiting for me to start the conversation. Where does one even begin?&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“I love your work! It’s an honor! I’m not going to ask you for any new facts, though I’ve read quite a few.”&lt;&#x2F;p&gt;
&lt;p&gt;“Oh yes, &lt;em&gt;most&lt;&#x2F;em&gt; of those are April Fool’s jokes.”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I stop and think. He probably has hundreds of conversations a week. The list of questions I had written down was looking less appealing by the minute. Quite obviously, I didn’t want to ask him about anything I could find on his Wikipedia page. I also didn’t want to ask him the same questions he gets every time he meets someone new. I didn’t want to talk about myself, because I am a fairly simple man. And I didn’t want to go off into the technical weeds, because like, he’s a smart guy but does he really want to talk about CRDTs with a stranger for the next 15 minutes of his life? Like, really?&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“So, uh, are you familiar with, I guess, CRDTs? Conflict-free replicated datatypes?” I ask.&lt;&#x2F;p&gt;
&lt;p&gt;“Like, the ones used in distributed consensus?”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Great start! Great start! Really what a great start!&lt;&#x2F;p&gt;
&lt;p&gt;We talked about CRDTs for a bit. Then he mentioned that he had seen my blog post about &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;slightknack.dev&#x2F;blog&#x2F;difflogic&#x2F;&quot;&gt;differentiable logic cellular automata&lt;&#x2F;a&gt;. Jeff’s really sharp, for what it’s worth. He makes connections quickly. For the original Google search index, I hear, he ran the first few iterations of PageRank in his head before manually writing out all the connections between all the websites on the internet and writing a program to do it.&lt;&#x2F;p&gt;
&lt;p&gt;We talked about different ways to quantize neural networks at first, and then the hardware to run them on; I asked him about some speculative alternative architectures I had heard whispers about, and he mused and pondered while I listened.&lt;&#x2F;p&gt;
&lt;p&gt;Halfway through, I interrupted,&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“I really am quite sorry. You see, I really am quite flustered. There are so many interesting technical conversations we could have, I’m sorry to ask you about topics you have thought about many times before.”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;He reassured me, and we chatted some more.&lt;&#x2F;p&gt;
&lt;p&gt;I asked him about his family, how he met my friend who put us in touch. I asked him if he happens to know my uncle, who works at Google. (To my surprise he did. My uncle designed Roboto, the font. Jeff said, “Roboto’s a nice font. Crisp. I use it for my, uh, slideshows and presentations sometimes.”)&lt;&#x2F;p&gt;
&lt;p&gt;If you could draw a path for our conversation it would trace a curve in some basis of { technical conversation, personal life }. That’s a pretty good basis, all things considered! It could have been worse. But I wish I would have picked a richer basis for our conversation to have unfolded in.&lt;&#x2F;p&gt;
&lt;p&gt;Were I to chat with Mr. Dean again, with the benefit of hindsight, I would ask him about, perhaps, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.theatlantic.com&#x2F;magazine&#x2F;archive&#x2F;2023&#x2F;09&#x2F;us-culture-moral-education-formation&#x2F;674765&#x2F;&quot;&gt;Moral Formation&lt;&#x2F;a&gt; (&lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;archive.is&#x2F;IBL1A&quot;&gt;archive&lt;&#x2F;a&gt;). Or the state of &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;americanaffairsjournal.org&#x2F;2023&#x2F;08&#x2F;americas-advanced-manufacturing-problem-and-how-to-fix-it&#x2F;&quot;&gt;advanced manufacturing in the US&lt;&#x2F;a&gt; (&lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;archive.is&#x2F;fX0O6&quot;&gt;archive&lt;&#x2F;a&gt;). Or perhaps the narrow intersection of &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;claytonchristensen.com&#x2F;wp-content&#x2F;uploads&#x2F;2012&#x2F;07&#x2F;Why_I_Belong_and_Why_I_Believe.pdf&quot;&gt;faith and reason&lt;&#x2F;a&gt;, which I find to be so richly compelling; and to hear his thoughts on which beliefs he finds to be compelling in turn. Or shared with him my favorite piece of short fiction, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;borretti.me&#x2F;fiction&#x2F;eog581&quot;&gt;&lt;em&gt;The Epiphany of Gliese 581&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;, and ask him whether he had read any interesting books worth reading recently.&lt;&#x2F;p&gt;
&lt;p&gt;Or perhaps some other topic that was interesting to both of us, that neither of us were experts in, as a substrate upon which we could exchange a larger conversation. Some object that we had different slices of, that we could put together to jointly imagine a picture of the whole.&lt;&#x2F;p&gt;
&lt;p&gt;I wish I could have asked more about his approach to life philosophy, or the type of future he wants to live in. The types of people worth building relationships with, and how to maintain them over many years. Though it was just a 15 minute conversation. Perhaps it was exactly what it needed to be. I wish it could have been more.&lt;&#x2F;p&gt;
&lt;p&gt;You see, I grew up moving around quite a bit, and I come from a big family. My siblings became my closest friends growing up, because they moved with me: I always had them to rely on when starting over in a new place. I knew that Jeff grew up moving around quite a lot too. I wonder what that was like for him. How it was similar; how it was different. I wish I would have asked.&lt;&#x2F;p&gt;
&lt;p&gt;Well, at the end of the day, life is long and there are many opportunities. If Jeff ever reads this, I promise I’m a better conversationalist. While this may have been our first conversation, I hope it isn’t our last.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Marathon Row</title>
        <published>2026-01-11T00:00:00+00:00</published>
        <updated>2026-01-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/daily/2026-01-11/"/>
        <id>https://slightknack.dev/daily/2026-01-11/</id>
        
        <content type="html" xml:base="https://slightknack.dev/daily/2026-01-11/">&lt;p&gt;Happy Sunday!&lt;&#x2F;p&gt;
&lt;p&gt;Two days ago, I woke up at 5:45am to row 26.2 miles. I walked on to the rowing team at MIT a year and a half ago. That might have been the greatest decision for personal development I have made. The row was hard work but I enjoyed it.&lt;&#x2F;p&gt;
&lt;p&gt;I really enjoy rowing. When you’re working hard there’s this acute pain and slow fatigue, but you learn to push through it and the feeling becomes normal, enjoyable even: it lets you know you’re engaged, it pushes you to row harder. If the row is already hurting, you might as well put in more work in so it goes by more quickly.&lt;&#x2F;p&gt;
&lt;p&gt;I have a few marathon projects I’ve been working on recently. One is Affetto, a systems programming language with affine algebraic effects. The other doesn’t really have a name yet, but it’s a distributed webassembly runtime, kind of like BEAM, that uses Wasm components as like the actor &#x2F; module boundary, and automatically derives networking &#x2F; serialization code from the WIT interface, so you can run apps across a cluster of nodes. It has dynamic addressing and supervisors and restarts works peer-to-peer and all that good stuff.&lt;&#x2F;p&gt;
&lt;p&gt;When I was in high school I was really into open source. (c.f. Passerine). I still really enjoy developing and working in the open. It would be something of a dream to be able to be paid to e.g. work full time on Affetto, or some other compiler.&lt;&#x2F;p&gt;
&lt;p&gt;I would also like to start a company, at some point. I have wanted to for a long time. I feel this natural clash of ethos between starting a company and pursuing working in the open. If I start a company, I want to do something meaningful. I believe that local-first software which preserves privacy and respects user freedom is something meaningful.&lt;&#x2F;p&gt;
&lt;p&gt;As the advice goes, “anyone can start a company; the challenge of designing a company is that of engineering an economic vehicle through which one can operationalize a change they wish to see in the world.” I don’t want to bet on the future with closed software. When I start a company, the software would have to be a complement of the main business, so I could release it openly. When I start a company, I might not even want to do software at all.&lt;&#x2F;p&gt;
&lt;p&gt;There’s this broader bits-to-atoms transition I’m seeing, as the egregore of the United States desires, to transition the economy back from services-based to mercantile. There’s this wonderful piece titled “America’s Advanced Manufacturing Problem—and How to Fix It”, it’s worth finding. According to the article, give or take: to secure an innovation lead post WWII, the US focused heavily on research, and moved the population from low-paying manufacturing jobs to high-paying knowledge work. Smart people these days work in software or finance as a result. A heavily-financialized economy can only look so good on paper. To me that means: we need more competition, consistent rules, and a game we can play worth winning. When you break the rules, the game stops, as Jean Baudrillard would say.&lt;&#x2F;p&gt;
&lt;p&gt;People demand too much of material reality. The machines have already mastered the dreamspace. The sublime. They are content wringing matter and creating wrought fantasy; they are content with simulation, we demand a collective fiction simulated on a material substrate. We demand houses and roads and experience and food and vacations. Yet despite this demand for the real we are so completely lost in the imagined we completely overlook the absurdity and collective insanity subsidizing our existence. It’s too complicated. It won’t win out in the long run. We may inherit the earth but it is a shallow inheritance if everything inherited is but a tiny distracted slice of the material universe. A game worth playing is one that forces the vast forces of the productive economy through the turnstiles of human flourishing. That’s the game we need to design if we wish to win the more we play it. I digress.&lt;&#x2F;p&gt;
&lt;p&gt;So on the atoms side of things, something meaningful worth doing would be acquiring or manufacturing many cheap solar panels and batteries to build out energy generation and storage infrastructure. This is one idea of billions, but the reality of Jevons dictates excitement.&lt;&#x2F;p&gt;
&lt;p&gt;Most startups fail for dumb reasons (poor leadership, sticking to an idea for too long, ignoring the customer, avoiding the economics). I don’t want to fixate on any one idea for now. Instead, I’d rather learn what it takes to run a company responsibly and efficiently; how to create and execute on a great opportunity quickly. To a certain extent, operational excellence requires being level-headed, assembling the right group of people, and being in the right place at the right time. Those are skills I can work to develop. I digress.&lt;&#x2F;p&gt;
&lt;p&gt;Perhaps because of all the work I expended rowing two days ago, I had a dream yesterday night. Dreams are rare, I dream infrequently. (We should stop conflating entering a state of REM with the experience of dreaming). In the dream I was on a competitive house-building team. We had to build a standard house to exact quality specifications as quickly as possible.&lt;&#x2F;p&gt;
&lt;p&gt;It is funny (to me) how much time people spend working out or in the gym. I’m one to blame: I do spend a lot of time working out (at least 20hr &#x2F; week); I do spend a lot of time in the gym. That work, however, goes nowhere. Imagine how much could be done with the calories we collectively expend to be measured by machines. How many houses could we build a day? I digress.&lt;&#x2F;p&gt;
&lt;p&gt;Two days ago, I completed a marathon row. Compared to running a marathon, it’s probably quite a bit easier. The water was glassy like a mirror; the sun was low and the wind was right. The best part, however, was setting out on an adventure with friends at sunrise, to do something together that we would never do alone. I’ve realized that I really can’t do anything alone. Doing anything incredible requires many people to pour their trust in you; I want to be a vessel worthy of holding that trust.&lt;&#x2F;p&gt;
&lt;p&gt;My friend &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ws-kj.github.io&#x2F;&quot;&gt;Will&lt;&#x2F;a&gt; and I are both very excited about infrastructure, manufacturing, and energy production. He switched from CS to Nuclear Engineering, to learn how to refine fuel. I’m still studying Math + CS, for the time being; the universe is computable and I’d like a strong understanding of rules so I can learn to play its game. When you break the rules, the game stops.&lt;&#x2F;p&gt;
&lt;p&gt;Today I fly to Greece for another adventure. That, however, will be a story for another time. We’ll see how long I’ll be able to keep up this daily post marathon. I promise I’ll get better over time. Today was a little all over the place. Thanks for sticking with me.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Inversions of Control</title>
        <published>2025-08-29T00:00:00+00:00</published>
        <updated>2025-08-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/inversion/"/>
        <id>https://slightknack.dev/blog/inversion/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/inversion/">&lt;p&gt;What’s the difference between a library and a framework? It depends on your definitions. Here are mine:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;When using a library, &lt;strong&gt;you are in control&lt;&#x2F;strong&gt;: a library provides a collection of behaviors you can choose to call.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;When using a framework, &lt;strong&gt;the framework is in control&lt;&#x2F;strong&gt;: a framework chooses to call a collection of behaviors you provide.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As a programmer, I prefer using libraries. It is nice to be in control, when the code you’re writing reads straightforwardly. On the other hand, as a library author, figuring out &lt;em&gt;how&lt;&#x2F;em&gt; to package a dependency as a library instead of as a framework can be challenging.&lt;&#x2F;p&gt;
&lt;p&gt;In this post, I want to show (1) how the relationship between frameworks and libraries has to do with &lt;strong&gt;inversions of control&lt;&#x2F;strong&gt; and (2) how languages can make inversions of control &lt;em&gt;easy&lt;&#x2F;em&gt;, so that authors can write frameworks, which developers can use as libraries.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;a-classic-problem&quot;&gt;A classic problem&lt;&#x2F;h1&gt;
&lt;p&gt;I’m working on a programming language called Affetto. It’s goal is to be “a smaller Rust”. The yet-unreleased compiler has Wasm-Component and C99-header-file backends. Affetto is a language for writing core libraries that can be embedded in other languages. Superficially, Affetto looks a little like Gleam. That shouldn’t really matter for the following examples, because I tried to stick to a simple syntax. I’m also not going to say anything about borrowing. I’ll write more about Affetto some other now, for the time being, consider this a small taste.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s say you’re writing a library that can do run-length encoding and decoding over streams of data. If you’re in control, writing a run-length encoder is fairly easy. Let’s say we are provided two callbacks, &lt;code&gt;recv&lt;&#x2F;code&gt; and &lt;code&gt;send&lt;&#x2F;code&gt;, that receive a byte and send a byte, respectively. Here’s how we could write an encoder:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span&gt;fun encode(
&lt;&#x2F;span&gt;&lt;span&gt;  recv: () -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;N8&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  send: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;N8 &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; (),
&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  mut last = recv()
&lt;&#x2F;span&gt;&lt;span&gt;  mut count = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;  loop {
&lt;&#x2F;span&gt;&lt;span&gt;    next = recv()
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; next != last or count == &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0xFF &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      send(last)
&lt;&#x2F;span&gt;&lt;span&gt;      send(count)
&lt;&#x2F;span&gt;&lt;span&gt;      set last = next
&lt;&#x2F;span&gt;&lt;span&gt;      set count = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      set count += &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Some affetto-specific notes: &lt;code&gt;N8&lt;&#x2F;code&gt; is an 8-bit natural, or a byte. &lt;code&gt;()&lt;&#x2F;code&gt; is the unit type, as in Rust. (like &lt;code&gt;void&lt;&#x2F;code&gt; in C, or &lt;code&gt;None&lt;&#x2F;code&gt; in Python).&lt;&#x2F;p&gt;
&lt;p&gt;This is fairly straightforward: we read bytes one at a time, we keep track of runs, we send two bytes for each run. (The byte and how many times it’s repeated).&lt;&#x2F;p&gt;
&lt;p&gt;The decoder, if anything, is even simpler:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span&gt;fun decode(
&lt;&#x2F;span&gt;&lt;span&gt;  recv: () -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;N8&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  send: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;N8 &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; (),
&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  loop {
&lt;&#x2F;span&gt;&lt;span&gt;    byte = recv()
&lt;&#x2F;span&gt;&lt;span&gt;    repeat = recv()
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt; in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;..repeat {
&lt;&#x2F;span&gt;&lt;span&gt;      send(byte)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The above is our framework for run-length encoding.&lt;&#x2F;p&gt;
&lt;p&gt;Now, the natural question becomes, let’s say I have some fountain-like source of bytes I’d like to encode, then decode, using the above framework. How would I go about doing it?&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span&gt;fun main() {
&lt;&#x2F;span&gt;&lt;span&gt;  source = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;  sink = fun(byte) -&amp;gt; debug(byte)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; then, um, huh?
&lt;&#x2F;span&gt;&lt;span&gt;  encode(source, todo)
&lt;&#x2F;span&gt;&lt;span&gt;  decode(todo, sink)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Obviously we’d need some sort of channel connecting &lt;code&gt;encode&lt;&#x2F;code&gt; and &lt;code&gt;decode&lt;&#x2F;code&gt;? And maybe threads, so the functions could run concurrently? Does affetto have coroutines, or async await? (Getting warmer.) What is to be done?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;invert-for-a-solution&quot;&gt;Invert for a solution&lt;&#x2F;h1&gt;
&lt;p&gt;Well, to begin, let’s try writing &lt;code&gt;decode&lt;&#x2F;code&gt; as a callback. It will have to close over some state, I suppose. Let’s call this function &lt;code&gt;decode_inverse&lt;&#x2F;code&gt;. &lt;code&gt;decode_inverse&lt;&#x2F;code&gt; will be called whenever &lt;code&gt;encode&lt;&#x2F;code&gt; calls &lt;code&gt;send&lt;&#x2F;code&gt;. This way the functions run in lock-step.&lt;&#x2F;p&gt;
&lt;p&gt;To create &lt;code&gt;decode_inverse&lt;&#x2F;code&gt;, first, we split &lt;code&gt;decode&lt;&#x2F;code&gt; at the matching calls to &lt;code&gt;recv&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; --- snip! state = 0
&lt;&#x2F;span&gt;&lt;span&gt;byte = recv()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; --- snip! state = 1
&lt;&#x2F;span&gt;&lt;span&gt;repeat = recv()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt; in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;..repeat {
&lt;&#x2F;span&gt;&lt;span&gt;  send(byte)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; --- snip!
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can lift this into a little state machine of sorts:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; state machine
&lt;&#x2F;span&gt;&lt;span&gt;b = recv()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; state {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    set byte = b
&lt;&#x2F;span&gt;&lt;span&gt;    set state = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    set repeat = b
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt; in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span&gt;.. repeat {
&lt;&#x2F;span&gt;&lt;span&gt;      send(byte)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    set state = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then wrap this up as a closure:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span&gt;fun decode_inverse(
&lt;&#x2F;span&gt;&lt;span&gt;  send: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;N8 &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; (),
&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  mut state = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;  mut byte = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; initial undefined value
&lt;&#x2F;span&gt;&lt;span&gt;  mut repeat = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; &amp;#39;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; implement send
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; fun(b) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; state machine
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; state {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        set byte = b
&lt;&#x2F;span&gt;&lt;span&gt;        set state = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        set repeat = b
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt; in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;..repeat {
&lt;&#x2F;span&gt;&lt;span&gt;          send(byte)
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;        set state = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I call this transformation &lt;strong&gt;an inversion of control&lt;&#x2F;strong&gt;. I find inversions of control to be a fundamental aspect of library design. They show up all the time, in all sorts of systems. Not always as closures: a natural next step is to convert &lt;code&gt;decode_inverse&lt;&#x2F;code&gt; into an object of some sort (struct + function). Sometimes libraries do this from the start. Closures are a poor man’s object, we’ll stick to closures for the time being.&lt;&#x2F;p&gt;
&lt;p&gt;This is a lot more complicated than the original code! And it closes over non-trivial state! This function, though, becomes something of a library. We can actually use it with &lt;code&gt;encode&lt;&#x2F;code&gt;, because it puts us in control. Here’s what that looks like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span&gt;fun main() {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; same setup
&lt;&#x2F;span&gt;&lt;span&gt;  source = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;  sink = fun(byte) -&amp;gt; debug(byte)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  encode(
&lt;&#x2F;span&gt;&lt;span&gt;    source,
&lt;&#x2F;span&gt;&lt;span&gt;    decode_inverse(sink),
&lt;&#x2F;span&gt;&lt;span&gt;  )
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Not super pretty, as &lt;code&gt;decode_inverse&lt;&#x2F;code&gt; is a closure passed as a callback, but it works. &lt;code&gt;encode&lt;&#x2F;code&gt; is in control, and drives &lt;code&gt;decode_inverse&lt;&#x2F;code&gt;. What about the other way around?&lt;&#x2F;p&gt;
&lt;p&gt;We could imagine applying a similar process to &lt;code&gt;encode&lt;&#x2F;code&gt; to create &lt;code&gt;encode_inverse&lt;&#x2F;code&gt;. We do this by splitting the function into a state machine at &lt;code&gt;send&lt;&#x2F;code&gt;. The resulting closure, &lt;code&gt;encode_inverse&lt;&#x2F;code&gt;, can be driven by &lt;code&gt;decode&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span&gt;decode(
&lt;&#x2F;span&gt;&lt;span&gt;  encode_inverse(source),
&lt;&#x2F;span&gt;&lt;span&gt;  sink,
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which can also be written without nesting:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span&gt;encoded = encode_inverse(source),
&lt;&#x2F;span&gt;&lt;span&gt;decode(encoded, sink)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And this code is still streaming the bytes as it encodes and decodes! &lt;code&gt;encoded&lt;&#x2F;code&gt; is a callback we can stream. We are not loading all the bytes into memory, which is great.&lt;&#x2F;p&gt;
&lt;p&gt;This “convert to state machine” transform seems pretty straightforward. Can we do it automatically? Before I answer that question, let’s explore one more aspect.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;higher-order-inversion&quot;&gt;Higher-order inversion&lt;&#x2F;h1&gt;
&lt;p&gt;Let’s say we have &lt;code&gt;encode_inverse&lt;&#x2F;code&gt; and &lt;code&gt;decode_inverse&lt;&#x2F;code&gt;. We want to wire them up to one another, as above. But in this case, it’s not exactly clear &lt;em&gt;who&lt;&#x2F;em&gt; is driving &lt;em&gt;who&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span&gt;fun main() {
&lt;&#x2F;span&gt;&lt;span&gt;  source = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;  sink = fun(byte) -&amp;gt; debug(byte)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  encoder = encode_inverse(source)
&lt;&#x2F;span&gt;&lt;span&gt;  decoder = decode_inverse(sink)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; then, um, huh?
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Well, what are the types of &lt;code&gt;encoder&lt;&#x2F;code&gt; and &lt;code&gt;decoder&lt;&#x2F;code&gt;?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;encoder&lt;&#x2F;code&gt; behaves as &lt;code&gt;recv&lt;&#x2F;code&gt;, so it’s &lt;code&gt;() -&amp;gt; N8&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;decoder&lt;&#x2F;code&gt; behaves as &lt;code&gt;send&lt;&#x2F;code&gt;, so it’s &lt;code&gt;N8 -&amp;gt; ()&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It seems like these types are compatible. We can drive this system with a loop:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;loop {
&lt;&#x2F;span&gt;&lt;span&gt;  decoder(encoder())
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If we wanted, we could lift this out as a higher-order function:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span&gt;fun pipe(
&lt;&#x2F;span&gt;&lt;span&gt;  recv: () -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;N8&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  send: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;N8 &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; (),
&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  loop {
&lt;&#x2F;span&gt;&lt;span&gt;    send(recv())
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And we could replace our loop in main with:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span&gt;pipe(encoder, decoder)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;multiple-inversions&quot;&gt;Multiple inversions&lt;&#x2F;h1&gt;
&lt;p&gt;We’ve been cheating a little. We’ve been assuming that we only care inverting a function along the axis of &lt;code&gt;send&lt;&#x2F;code&gt; or &lt;code&gt;recv&lt;&#x2F;code&gt;. What if we want a function that’s inverted for &lt;em&gt;both&lt;&#x2F;em&gt; send and receive? Let’s start once again with &lt;code&gt;decode&lt;&#x2F;code&gt;. We’ll write a function called &lt;code&gt;decode_actor&lt;&#x2F;code&gt;. It’s been a while, so here’s the code:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span&gt;fun decode(
&lt;&#x2F;span&gt;&lt;span&gt;  recv: () -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;N8&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  send: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;N8 &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; (),
&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  loop {
&lt;&#x2F;span&gt;&lt;span&gt;    byte = recv()
&lt;&#x2F;span&gt;&lt;span&gt;    repeat = recv()
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt; in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;..repeat {
&lt;&#x2F;span&gt;&lt;span&gt;      send(byte)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As before, we need to slice this into a state machine, but at both &lt;code&gt;recv&lt;&#x2F;code&gt; points and &lt;code&gt;send&lt;&#x2F;code&gt; points:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; --- snip! state = 0
&lt;&#x2F;span&gt;&lt;span&gt;byte = recv()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; --- snip! state = 1
&lt;&#x2F;span&gt;&lt;span&gt;repeat = recv()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt; in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;..repeat {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; --- snip! state = 2
&lt;&#x2F;span&gt;&lt;span&gt;  send(byte)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; --- snip! state = 0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We have a for loop here, which for reasons that will become clear later, we will also need to handle. Let’s “desugar” the for loop:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; --- snip! state = 0
&lt;&#x2F;span&gt;&lt;span&gt;byte = recv()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; --- snip! state = 1
&lt;&#x2F;span&gt;&lt;span&gt;repeat = recv()
&lt;&#x2F;span&gt;&lt;span&gt;mut i = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; --- snip! state = 2
&lt;&#x2F;span&gt;&lt;span&gt;send(byte)
&lt;&#x2F;span&gt;&lt;span&gt;set i += &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; i &amp;lt; repeat {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; state = 2
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; --- snip! state = 0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can now lift this into a state machine:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; --- snip! state = 0
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  set byte = recv()
&lt;&#x2F;span&gt;&lt;span&gt;  set state = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  set repeat = recv()
&lt;&#x2F;span&gt;&lt;span&gt;  set i = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;  set state = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  send(byte)
&lt;&#x2F;span&gt;&lt;span&gt;  set i += &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; i &amp;lt; repeat {
&lt;&#x2F;span&gt;&lt;span&gt;    set state = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    set state = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, here’s a conundrum: Our state machine calls &lt;code&gt;recv&lt;&#x2F;code&gt; (&lt;code&gt;R&lt;&#x2F;code&gt;) and &lt;code&gt;send&lt;&#x2F;code&gt; (&lt;code&gt;S&lt;&#x2F;code&gt;) in a specific order. The order of calls looks like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;R R S... R R S... R R S...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This ordering requires that our code call the state machine with &lt;code&gt;R&lt;&#x2F;code&gt; and &lt;code&gt;S&lt;&#x2F;code&gt; in the right order! Unlike the case where there was only one callback we were lifting, we’re now faced with a choice to make. How do we represent an object, with some state, with different ways to call it depending on the state it’s in?&lt;&#x2F;p&gt;
&lt;p&gt;If we push this deeper, we stumble upon some beautiful symmetries: actors are a generalization of closures, the purpose of protocols (&lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;clojure.org&#x2F;reference&#x2F;protocols&quot;&gt;as in clojure&lt;&#x2F;a&gt;), &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cliffle.com&#x2F;blog&#x2F;rust-typestate&#x2F;&quot;&gt;type-state programming&lt;&#x2F;a&gt;, the sequencing of algebraic effects, and so on.&lt;&#x2F;p&gt;
&lt;p&gt;I hate to end on a cliff-hanger, but I would like to get this piece published, as it’s been sitting on my disk for about a month. In the next post, we will relate inversions of control to Algebraic Effects in Affetto. Stay tuned!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Compiling a Neural Net to C for a 1,744× speedup</title>
        <published>2025-05-27T00:00:00+00:00</published>
        <updated>2025-05-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/difflogic/"/>
        <id>https://slightknack.dev/blog/difflogic/</id>
        
        <summary type="html">&lt;p&gt;&lt;strong&gt;tl;dr:&lt;&#x2F;strong&gt; I trained a neural network (NN), with logic gates in the place of activation functions, to learn a 3×3 kernel function for Conway’s Game of Life. I wanted to see if I could speed up inference by extracting the learned logic circuit from the NN. So, I wrote some code to extract and compile the extracted logic circuit to bit-parallel C (with some optimizations to remove gates that don’t contribute to the output). I benchmarked the original NN against the extracted 300-line single-threaded C program.; compiling the NN to C resulted in a 1,744× speedup! Crazy, right? &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;difflogic&quot;&gt;Here’s the repo&lt;&#x2F;a&gt;: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;difflogic&#x2F;blob&#x2F;6f01c83a0e6d02dcec59ab91c64eaf91ee4a3776&#x2F;main.py&quot;&gt;~354 lines of Python&#x2F;JAX&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;difflogic&#x2F;blob&#x2F;master&#x2F;gate.c&quot;&gt;~331 lines of C&lt;&#x2F;a&gt;, if you want to &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;difflogic&#x2F;tree&#x2F;89e2ed2f2c018122132598ea610a960900079bea?tab=readme-ov-file#to-reproduce&quot;&gt;reproduce it&lt;&#x2F;a&gt; and&#x2F;or mess around.&lt;&#x2F;p&gt;
&lt;!-- &lt;iframe src=&quot;&#x2F;conway&quot; width=&quot;100%&quot; frameborder=&quot;0&quot;&gt;&lt;&#x2F;iframe&gt; --&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Seeds of a Digital Garden</title>
        <published>2025-04-25T00:00:00+00:00</published>
        <updated>2025-04-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/garden/"/>
        <id>https://slightknack.dev/blog/garden/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/garden/">&lt;div class=&quot;boxed&quot;&gt;
&lt;p&gt;&lt;em&gt;N.B.&lt;&#x2F;em&gt; I’m publishing some gems from writing I did over the course of two years while living in Brazil. Read &lt;a href=&quot;&#x2F;blog&#x2F;writing&quot;&gt;the first post&lt;&#x2F;a&gt; for more context.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;p class=&quot;tag&quot;&gt;2022-12-03 · Isaac Clayton · 87 minutes spent&lt;&#x2F;p&gt;
&lt;p&gt;One problem I often run into when I start writing is that I have either
too &lt;em&gt;few&lt;&#x2F;em&gt; ideas or too &lt;em&gt;many&lt;&#x2F;em&gt;. To put an idea on a page, we have to
isolate it, find its form, and pin it down. Unfortunately, ideas are
slippery things: it is hard to straighten a slippery tangle of thoughts
into a single, coherent thread. We want to have clear ideas to write
about, because clear ideas lead to clear writing.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;a-garden-in-the-wilderness&quot;&gt;A garden in the wilderness&lt;&#x2F;h1&gt;
&lt;p&gt;To that end, suppose our mind is a little patch of wilderness in which
wild ideas take root. Though many interesting ideas may find fertile
ground, they will be choked out unless tended to. If our goal is to
consistently harvest the best ideas to write about, we have to gradually
cultivate this wilderness until it resembles something of a garden. Like
a gardener, we can’t wrestle this tangle of weeds and ideas with bare
hands alone: we need gloves and tools to direct and magnify our efforts.&lt;&#x2F;p&gt;
&lt;p&gt;So in this essay, I hope to outline some techniques we can use and
habits we can build to tend to our digital garden.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;squaring-off-a-plot&quot;&gt;Squaring off a plot&lt;&#x2F;h1&gt;
&lt;p&gt;A digital garden is a digital space we use to cultivate ideas. Like any
good gardener, before we plant any seeds, we must first plot out our
garden and prepare the soil for planting. It really doesn’t matter where
you start your digital garden, because unlike physical gardens, digital
gardens are relatively easy to move around and replant elsewhere. So
hide the clutter on your desktop, open up a fresh browser pane, create a
new document—text file, Google doc, Notion block, or otherwise—and start
plotting out your garden.&lt;&#x2F;p&gt;
&lt;p&gt;Things will be a little messy at first, but we’re dealing with plants
and dirt, not permanent stone. It’s okay to make mistakes. The first
thing you’re going to want to do, though, is to make your digital garden
easily accessible. Don’t leave it floating in cyber-space, lay down a
little path between your home (screen) and your (digital) garden.
Whether that be a bookmark in your browser or alias on your desktop is
up to you, just make sure you keep the path visible and well-groomed,
lest it becomes overgrown with wayward bits.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;questions-are-seeds-for-ideas&quot;&gt;Questions are seeds for ideas&lt;&#x2F;h1&gt;
&lt;p&gt;If our goal is to harvest ideas, we need some sort of seeds from which
ideas spring. What better seed exists than those of &lt;em&gt;Questions&lt;&#x2F;em&gt;?&lt;&#x2F;p&gt;
&lt;p&gt;This first document you’ve created (the first of many) will be a little
nursery for the seeds you encounter while jungling through The Internet.
This is where your &lt;em&gt;Questions&lt;&#x2F;em&gt; go.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;When creating a new page in my digital garden, I like to start with a
title, a date, and a short description of how the document is organized,
roughly. While this description acts as loose schema now, over time the
organization of the document will become entrenched as we build habits
respecting it.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Your &lt;em&gt;Questions&lt;&#x2F;em&gt; document doesn’t have to be anything fancy… &lt;em&gt;at first&lt;&#x2F;em&gt;.
It could start as a list of questions, and the date they were asked on,
for instance. (Recording dates is important: it is better to have a date
and not need it than to need a date and not have it.)&lt;&#x2F;p&gt;
&lt;p&gt;How to find questions is, well, another question, of course. (Maybe you
could add that to your &lt;em&gt;Questions&lt;&#x2F;em&gt; page!) While browsing the interwebs,
just keep your &lt;em&gt;Questions&lt;&#x2F;em&gt; page open in the background: when a moment of
inspiration strikes, you now have a place to reify that seed of thought:
write it down.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;nursing-seeds-into-outlines&quot;&gt;Nursing seeds into outlines&lt;&#x2F;h1&gt;
&lt;p&gt;Questions are important, but alone they are just seeds. We need to plant
them, water them, give them plenty of sun, and a little space to grow.
Enter, the humble &lt;em&gt;Outline&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;An outline is nothing fancy. It is a supple collection of phrases,
arranged in hierarchy, tracing out the hills and valleys of a particular
thread of thought. Find your most interesting questions (perhaps marking
them with a star): these are seeds you are ready to plant.&lt;&#x2F;p&gt;
&lt;p&gt;For each ready seed, you can sketch an outline. When and where you
sketch an outline are up to you. You could set a daily goal, do it as
you come across new ideas, directly in-line, or on a new page. As you
sketch more outlines, you will get better at organizing them.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;from-nursery-to-garden-proper&quot;&gt;From nursery to garden proper&lt;&#x2F;h1&gt;
&lt;p&gt;Once an outline gets too large to conveniently exist in line, it is
definitely time to move it to another page. So create a new document,
and &lt;em&gt;copy&lt;&#x2F;em&gt; the question and outline there. You are now free to remove
the outline from your &lt;em&gt;Questions&lt;&#x2F;em&gt; document, but be sure to &lt;em&gt;link&lt;&#x2F;em&gt; to the
new page!&lt;&#x2F;p&gt;
&lt;p&gt;Figuring out how to make a strong link is important. If your digital
garden exists on the web, you are lucky that hyperlinks exist. If you
have used a text file, you might be experiencing some growing pains.
These pains are natural, and indeed… oddly desirable. Look to figure out
how to make links work: you could host your text file on the web, switch
to a tool like Obsidian, use a fancier text editor like Emacs (with the
right plugins), or roll something of a home-grown solution, a program of
your own. (A word of warning: you are building a digital garden, not
something that builds digital gardens. Forsake abstraction: do not lose
sight of your original goal.)&lt;&#x2F;p&gt;
&lt;p&gt;Once you have plotted out a &lt;em&gt;Questions&lt;&#x2F;em&gt; page and a manner to link to
your steadily-growing outlines, remember that a garden requires care and
weeding. Some outlines will grow blazingly fast, and start to deviate
from the original question they were designed to answer. You must weed
and prune your outlines.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pruning-and-replanting&quot;&gt;Pruning and replanting&lt;&#x2F;h1&gt;
&lt;p&gt;Some growths within an outline are interesting in their own right. A
weed is only a plant that is growing in the wrong place: that plant
might have a home elsewhere in your garden. Thoroughly weed and prune
your outline, keeping it healthy. Move interesting seeds to their own
plot; cast any wicked tangents into the composting bin. It is important
to keep a tidy garden: we are trying to &lt;em&gt;tame&lt;&#x2F;em&gt; the wild growth rather
than letting it grow unbounded.&lt;&#x2F;p&gt;
&lt;p&gt;Our sprouting outlines are cute and fun to care for, but hold no weight
as a productive plant that will bear fruit. Our ideas have not yet taken
root. We must allow young ideas to settle more permanently in the soil
of our minds. Like a plant spreading its leaves and taking root, we must
turn our outline into weightier paragraphs.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;from-outline-to-essay&quot;&gt;From outline to essay&lt;&#x2F;h1&gt;
&lt;p&gt;Once pruned, supported, and refined, an outline should hold a single,
easy-to-follow thread of thought that marks the shape of a fully-grown
plant. The thread of an outline should fully address the question at
hand. Once ready, grow an outline into paragraphs by translating one
point at a time, each point mapping to a paragraph. Although the growth
of paragraphs may be a little wild and uncultivated, it is best to let
them grow out fully, lest they wither because of premature pruning.&lt;&#x2F;p&gt;
&lt;p&gt;Once an idea has taken root and sprouted into a wild mass of greenery,
it is time to prune and refine our plant until we coax out the fruit of
our labors. Reading the unfurled outline top to bottom, we can discover
inconsistencies, dead branches, unneeded explanation, and lopsided
details. With a shear in hand, trim out these wayward paragraphs. Though
it is hard to remove writing, do it for the good of the idea as a whole.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;seeds-of-new-ideas-the-fruit-of-your-labor&quot;&gt;Seeds of new ideas, the fruit of your labor&lt;&#x2F;h1&gt;
&lt;p&gt;At this stage you should have a healthy plant growing firmly in a corner
of your garden. The fruit of your labors is a complete answer to the
question you originally posed, which hopefully contains many seeds for
future questions. It may be tempting to leave this answer as-is, but we
are far from done. A good gardener knows it is best to continue to care
for even fully-grown plants.&lt;&#x2F;p&gt;
&lt;p&gt;Revisit old ideas from time to time. Let them grow out a little more,
then prune and refine them. Gradual editing and improvement is key to
caring for healthy plants. Lay down paths within your garden, linking
ideas together. Add signs and labels to make it harder to get lost. Add
benches as points to rest and admire. Every garden is a little
different, from backyard to botanical; work on your garden daily, and
make it your own.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;tending-to-a-growing-garden&quot;&gt;Tending to a growing garden&lt;&#x2F;h1&gt;
&lt;p&gt;Although questions are effective seeds, they are far from the only seeds
that exist. As part of the questions you ask yourself, ask questions as
to how you can improve your garden. Building a garden is more than just
planting seeds: it requires building fences, laying paths, careful
weeding, and so on. You will sweat a little.&lt;&#x2F;p&gt;
&lt;p&gt;To extend your garden, you could start recording the things you read
alongside the questions that arise. You could start keeping a journal to
record what happens each day. You could embark on larger projects,
linking together a series of ideas to landscape a larger vista. Don’t
limit yourselves to words alone. Ideas come in far more shapes than
lines of symbols on a page. Figure out how to draw. How to link
sequences of drawings together. How to create little interactive worlds
that illustrate ideas better than any words or pictures could. This may
be hard; the tools you might need might not exist yet. Don’t let that
stop you.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>We need an LLVM project for browser engines</title>
        <published>2025-04-18T00:00:00+00:00</published>
        <updated>2025-04-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/browser/"/>
        <id>https://slightknack.dev/blog/browser/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/browser/">&lt;div class=&quot;boxed&quot;&gt;
&lt;p&gt;&lt;em&gt;N.B.&lt;&#x2F;em&gt; I’m publishing some gems from writing I did over the course of two years while living in Brazil. Read &lt;a href=&quot;&#x2F;blog&#x2F;writing&quot;&gt;the first post&lt;&#x2F;a&gt; for more context.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;p class=&quot;tag&quot;&gt;2023-09-13 · Isaac Clayton&lt;&#x2F;p&gt;
&lt;p&gt;You’re reading this in Chrome. Or, probably Firefox. Or Safari. Heaven-forbid Edge (only joking).&lt;&#x2F;p&gt;
&lt;p&gt;There is a web browser hegemony. &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Usage_share_of_web_browsers#Summary_tables&quot;&gt;More than 60% of the world uses Chrome&lt;&#x2F;a&gt;. As we’ve seen with recent proposals for &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arstechnica.com&#x2F;gadgets&#x2F;2023&#x2F;07&#x2F;googles-web-integrity-api-sounds-like-drm-for-the-web&#x2F;&quot;&gt;Web Integrity&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.osnews.com&#x2F;story&#x2F;136502&#x2F;apple-already-shipped-attestation-on-the-web-and-we-barely-noticed&#x2F;&quot;&gt;Attestation&lt;&#x2F;a&gt;, and the antonymic meme that is the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arstechnica.com&#x2F;gadgets&#x2F;2023&#x2F;09&#x2F;googles-widely-opposed-ad-platform-the-privacy-sandbox-launches-in-chrome&#x2F;&quot;&gt;Privacy Sandbox&lt;&#x2F;a&gt;, all roads from a browser monopoly lead to undesirable outcomes. How can we keep the open web resilient against the whims of a few vendors?&lt;&#x2F;p&gt;
&lt;p&gt;Everyone uses Chrome because &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;598841&#x2F;how-to-get-started-building-a-web-browser&quot;&gt;writing a browser is &lt;em&gt;hard&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; (and at some point, Chrome sucked the least). The Web Standard is incredibly &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.w3.org&#x2F;standards&#x2F;&quot;&gt;huge&lt;&#x2F;a&gt;. It has many &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;html.spec.whatwg.org&#x2F;#obsolete&quot;&gt;quirks&lt;&#x2F;a&gt;; major browsers &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;v4.chriskrycho.com&#x2F;2017&#x2F;chrome-is-not-the-standard.html&quot;&gt;deviate&lt;&#x2F;a&gt; from the standard. A proper ‘modern’ browser is, frankly, too big for any one individual to implement. If an individual could write a full browser in a weekend, we might not have this problem. Sounds impossible, right?&lt;&#x2F;p&gt;
&lt;p&gt;Let’s talk about programming languages. Writing an optimizing compiler backend &lt;em&gt;used&lt;&#x2F;em&gt; to be hard, too. There are a number of instruction sets, hundreds of platforms, and thousands of optimizations that can be applied. Where to begin? For a long time there was something of a language monopoly. Any hobbyist could hack together a lisp interpreter, but a &lt;em&gt;Real Language&lt;&#x2F;em&gt; required a &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;isocpp.org&#x2F;std&#x2F;the-committee&quot;&gt;committee&lt;&#x2F;a&gt; or a &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Sun_Microsystems&quot;&gt;corporate sponsor&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This all changed after &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;llvm.org&quot;&gt;LLVM&lt;&#x2F;a&gt;. LLVM is a &lt;em&gt;universal compiler backend&lt;&#x2F;em&gt;. LLVM started its life as the backend for &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;clang.llvm.org&quot;&gt;clang&lt;&#x2F;a&gt;, the C compiler. It has since been picked up by a number of your favorite projects, including &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;rustc-dev-guide.rust-lang.org&#x2F;backend&#x2F;codegen.html&quot;&gt;Rust&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;swiftlang&#x2F;llvm-project&quot;&gt;Swift&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.julialang.org&#x2F;en&#x2F;v1&#x2F;devdocs&#x2F;llvm&#x2F;&quot;&gt;Julia&lt;&#x2F;a&gt;, and &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;kristoff.it&#x2F;blog&#x2F;zig-new-relationship-llvm&#x2F;&quot;&gt;Zig&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;LLVM is a big tool that does a lot of heavy lifting. It’s popular because (compared to writing an optimizing compiler) it’s very easy to use. There’s an &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;llvm.org&#x2F;docs&#x2F;tutorial&#x2F;&quot;&gt;official guide&lt;&#x2F;a&gt; showing how, in about a weekend, one may implement a small language (Kaleidoscope) that &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;llvm.org&#x2F;docs&#x2F;tutorial&#x2F;MyFirstLanguageFrontend&#x2F;LangImpl08.html&quot;&gt;compiles to machine code&lt;&#x2F;a&gt;, with &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;llvm.org&#x2F;docs&#x2F;tutorial&#x2F;MyFirstLanguageFrontend&#x2F;LangImpl04.html&quot;&gt;optimization passes&lt;&#x2F;a&gt;, that can &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;llvm.org&#x2F;docs&#x2F;tutorial&#x2F;MyFirstLanguageFrontend&#x2F;LangImpl03.html&quot;&gt;interact with existing C libraries&lt;&#x2F;a&gt;. LLVM grows with you: keep at it for a few months, and you’ll have a full compiler on your hands.&lt;&#x2F;p&gt;
&lt;p&gt;In this sense, programming languages have been democratized. Anyone can write a compiler for any language. Better yet, we have hundreds of competing languages. As the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;craftinginterpreters.com&quot;&gt;pedagogical ramp&lt;&#x2F;a&gt; improves, creating a new programming language will only become easier. Now, what of the web?&lt;&#x2F;p&gt;
&lt;p&gt;To keep the dream of the open web alive, we need something of a &lt;em&gt;universal browser backend&lt;&#x2F;em&gt;. Call it an LLVM for browsers:&lt;&#x2F;p&gt;
&lt;p&gt;This universal browser backend would have to implement a large useful subset of the web standard. Ideally as many composable componentized modules, possessing simple interfaces, with bindings for many languages.&lt;&#x2F;p&gt;
&lt;p&gt;Developers could ‘mix and match’ modules for https, layout, rendering, JS, websockets, Wasm, and so on to develop their own browser engine. Library authors could write higher-level interfaces that help people to compose their own browser from greater component parts. Using such libraries, people could write a browser in a weekend, and, with a little work, a full browser in a few months.&lt;&#x2F;p&gt;
&lt;p&gt;Just as the diversity of popular languages has exploded since the arrival of LLVM, we should expect an equivalent explosion of the types of browsers available once a universal browser backend arrives. Anyone could write a browser for any subset (or superset!) of standards, and we would have hundreds of competing browsers, filling hundreds of different niches. The web would fulfill its goal of becoming a &lt;em&gt;universal application platform&lt;&#x2F;em&gt;. What type of browser would you write?&lt;&#x2F;p&gt;
&lt;p&gt;A universal browser engine would need a project to drive it forward. Before LLVM’s cambrian explosion, it was a backend for clang, a standard C compiler. Likewise, for a universal browser backend to happen, we’ll need ‘a clang’: a standards-compliant tabbed web browser driving development forward. This ‘clang’-type project could come from:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;An independent organization, with a lot of capital, that wants to break into the browser market (e.g. Microsoft).&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Motivated developers, modularizing the implementation of an existing browser engine like Firefox or Chromium.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Browser engineers, starting a &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ladybird.dev&quot;&gt;new project&lt;&#x2F;a&gt;, built from the ground up, that has reached escape velocity and has paved the road for others to do the same.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;You are in a position to do something, right now. If you’re reading this in Chrome, stop. Please give &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.mozilla.org&#x2F;en-US&#x2F;firefox&#x2F;new&#x2F;&quot;&gt;Firefox&lt;&#x2F;a&gt;—or &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;lynx.browser.org&quot;&gt;literally&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;browser.kagi.com&quot;&gt;any&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arc.net&quot;&gt;other&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;SerenityOS&#x2F;ladybird&quot;&gt;browser&lt;&#x2F;a&gt;—a try.&lt;&#x2F;p&gt;
&lt;p&gt;If you’re curious about how browsers work, try implementing one. There are plenty of guides: here’s a &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;limpet.net&#x2F;mbrubeck&#x2F;2014&#x2F;08&#x2F;08&#x2F;toy-layout-engine-1.html&quot;&gt;good place to start&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you work on browsers, share what you know with others. I fell in love with computers reading blogs and exploring the better parts of the open web. I learned things I wouldn’t’ve learned otherwise. It pains me to see people using their web-browser without an adblocker, or running into broken websites with no idea how to fix them. I like to think about the web I would want the &lt;em&gt;next generation&lt;&#x2F;em&gt; to explore. I would like to work towards realizing that vision. As a kid, one of the things that annoyed me most was that it cost money to &lt;del&gt;buy&lt;&#x2F;del&gt; rent a domain name. Why limit browsers to http via dns? What if kids could write &lt;em&gt;real websites&lt;&#x2F;em&gt; in an editor based on Scratch, shared P2P in a little community? What if there was a standard protocol for extending browsers  with new protocols and presentation formats (in a secure sandboxed manner)? What wonderful types of application platforms and paradigms exist but are yet to be discovered? (Or have been lost and must be &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amber-lang.net&#x2F;&quot;&gt;rediscovered&lt;&#x2F;a&gt;?) As the open web becomes ravaged by scrapers and bots, a tight-knit web of alternate browser communities might be woven together. What primitives might a browser expose if it were &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Beaker_(web_browser)&quot;&gt;built to support local-first software by default&lt;&#x2F;a&gt;? What powerful content-presentation tools would a browser expose if enabling online advertizing weren’t a priority? The sky is the limit! We’re counting on you!&lt;&#x2F;p&gt;
&lt;p&gt;There is a browser hegemony: it’s stifling innovation and limiting the web’s potential of becoming a universal application platform. Just as LLVM made it possible for anyone to write a compiler, we need a &lt;em&gt;universal browser backend&lt;&#x2F;em&gt;, so that &lt;em&gt;anyone&lt;&#x2F;em&gt; can write a browser. What type of browser would you write?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;postscript-solidarity&quot;&gt;Postscript, Solidarity&lt;&#x2F;h1&gt;
&lt;p class=&quot;tag&quot;&gt;2025-04-18 · Isaac Clayton&lt;&#x2F;p&gt;
&lt;p&gt;In 2020, I started messing around with a project I called &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sldty&quot;&gt;Solidarity&lt;&#x2F;a&gt;. It was more of a proof-of-concept; I haven’t worked on it in years. The goal was to build a fun exploratory alternate to the web, from first principles. As a joke, I called it “The Internet, 2”.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to write a browser where a client-server architecture was obsolete, so over-the-network collaboration was the default. To that end, Solidarity introduced a new protocol, &lt;code&gt;kitbag&lt;&#x2F;code&gt;, for realtime P2P replication of data over UDP; an unfinished crdt library, &lt;code&gt;together&lt;&#x2F;code&gt;, for data synchronization; a self-describing binary data format, &lt;code&gt;d3t&lt;&#x2F;code&gt;, for encoding messages for the network; a WebAssembly runtime for running apps and browser components (named &lt;code&gt;jiggle&lt;&#x2F;code&gt;, I guess?); an implementation of a distributed consensus algorithm, &lt;code&gt;drop-in-fba&lt;&#x2F;code&gt;, for creating a global linear order of events (when CRDTs fell short). On top of this, systems for assigning names to content-addresses could be built.&lt;&#x2F;p&gt;
&lt;p&gt;Everything was content-addressed, so apps and static assets were replicated to your device on demand. All parts of the browser ran as little components compiled to WebAssembly, exposing interfaces to interact with other components and the host system. The browser could be extended with new protocols and formats by installing an app with the right capabilities. The browser was also the application editor. As easily as you could download an app, you could also fork an app and edit it in-browser to work however you wanted it to!&lt;&#x2F;p&gt;
&lt;p&gt;There were lots of other ideas in the works. Instead of html and css, applications could be written in any language (compiled to Wasm), and would render pages by interfacing with a WebGPU component (also providing an accessibility tree, of course). There was no such thing as a ‘per-website account’: only a browser-wide component providing high-level primitives for identity, through public-key cryptography. Browsers weren’t limited to a single machine: if you had a beefy home server you owned (or, say, a desktop tied to your phone), apps could offload computationally-intensive tasks to run on the more powerful machine (application code and processed data automatically being replicated between the beefy computer and your edge device using &lt;code&gt;kitbag&lt;&#x2F;code&gt;, of course). If you wanted to, you could rent, say, a beefy computer from aws; this is your homeserver; you control what runs on it. All the benefits of the cloud, none of the drawbacks.&lt;&#x2F;p&gt;
&lt;p&gt;The foundation, &lt;code&gt;kitbag&lt;&#x2F;code&gt;, is a protocol for real-time data sync. All apps are collaborative and local-first by default. If an app pointed two different computers at the same &lt;code&gt;kitbag&lt;&#x2F;code&gt; document, the computers would find each other, sync up, and start exchanging edits. What if making an application collaborative was as easy as telling the browser, “yes, both these identities can now edit the same document”, and it &lt;em&gt;just worked&lt;&#x2F;em&gt;?&lt;&#x2F;p&gt;
&lt;p&gt;I’m still very excited by this idea, and perhaps one day I’ll write it up in full. If the web ever becomes unbearable, I’ll build Solidarity for my friends. I hope we never reach that point :)&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Piano-Sleep Effect</title>
        <published>2025-04-17T00:00:00+00:00</published>
        <updated>2025-04-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/sleep/"/>
        <id>https://slightknack.dev/blog/sleep/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/sleep/">&lt;p&gt;An old note. I’m publishing a backlog of writing I’ve done. Read &lt;a href=&quot;&#x2F;blog&#x2F;writing&quot;&gt;the first post&lt;&#x2F;a&gt; for more context.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p class=&quot;tag&quot;&gt;2024-03-26 · Isaac Clayton&lt;&#x2F;p&gt;
&lt;p&gt;I noticed something while playing the piano a while back that I thought was pretty interesting. It has to do with how I practice and remember things.&lt;&#x2F;p&gt;
&lt;p&gt;I play piano mostly by memorization. I can sorta read sheet music: I can read and play with one hand just fine, either left or right, but I can’t read both hands at the same time. Putting them together takes practice. I can memorize music about as fast as I can learn to play it.&lt;&#x2F;p&gt;
&lt;p&gt;Here’s the cool part, which I’m calling the piano sleep effect. On any given day, I can practice as much as I want, but I will generally plateau at a certain level. When I go to sleep and wake up the next day, my yesterday’s best becomes my today’s baseline. Which is to say, if I wake up, sit down, and play the piece, I’ll usually play about as good as my best run yesterday.&lt;&#x2F;p&gt;
&lt;p&gt;This is interesting for a number of reasons. For one, it suggests that there is sort of a saturation limit for how much I can improve by practicing any given part of a song in one day. But also, if I learn one bar or 20, one song or three, all will have improved when I go to sleep and wake up.&lt;&#x2F;p&gt;
&lt;p&gt;For two, this depends on whether I practice things correctly. For example, if I learn how to play a section slowly, but with the right notes and timing, the next day I can play it fast and correctly. If I practice playing a section fast, but wrong, I won’t learn how to play it right the next day just by sleeping.&lt;&#x2F;p&gt;
&lt;p&gt;For three, this suggests that each day I should focus on: replaying what I learned yesterday for the gains on those sections, but playing them no more than that, and saturating my brain with as many new sections as possible. Focusing on playing them uniformly and correctly, especially the sections that have not been saturated yet. Across multiple songs, even.&lt;&#x2F;p&gt;
&lt;p&gt;Another tangential but related effect: if I saturate with one piece and return to a piece I know how to play, I will play that piece really well. As in I will play with an extreme amount of accuracy. I guess this could fall under warming up, but it feels more pronounced than, e.g. playing the same piece to warm up.&lt;&#x2F;p&gt;
&lt;p&gt;Of course none of this is scientific or rigorous. But if it works for me that’s all that matters. N = 1 is alright if N = you.&lt;&#x2F;p&gt;
&lt;p&gt;Getting a good night’s rest is magic for the brain. Have you seen anything similar in your life?&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Replication &gt; REST</title>
        <published>2025-04-16T00:00:00+00:00</published>
        <updated>2025-04-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/replication/"/>
        <id>https://slightknack.dev/blog/replication/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/replication/">&lt;blockquote&gt;
&lt;p&gt;I’m publishing a backlog of writing I’ve done. Read &lt;a href=&quot;&#x2F;blog&#x2F;writing&quot;&gt;the first post&lt;&#x2F;a&gt; for more context. On today’s menu: I have been thinking about CRDTs and synchronization for a few years (e.g. this &lt;a href=&quot;&#x2F;blog&#x2F;backing-crdt-store&quot;&gt;old post&lt;&#x2F;a&gt; about CRDTs). I’m a fan also of functional-relational programming (but think in its purest form it can look &lt;em&gt;so much cooler&lt;&#x2F;em&gt; than React). In the post that follows, I try to pull some of these disparate threads together. Enjoy!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;stop-building-databases&quot;&gt;Stop Building Databases&lt;&#x2F;h1&gt;
&lt;p class=&quot;tag&quot;&gt;
2023-12-02 · Isaac Clayton · In response to &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;sqlsync.dev&#x2F;posts&#x2F;stop-building-databases&#x2F;&quot;&gt;Stop Building Databases&lt;&#x2F;a&gt;
&lt;p&gt;It pains me to admit how long it took for me to understand the &lt;em&gt;raison
d’être&lt;&#x2F;em&gt; of databases. For the longest time I thought of databases as
bloated storage engines for “webscale” projects. This dangerous line of
thought may be due to my exposure to database ORMs in Python
&lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Tloru&#x2F;NovelWrite&quot;&gt;while using Flask as a child&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;(I really didn’t understand what I was doing at the time and just
copy-pasted ORM code and SQLite migrations, praying that it would work.
When the migration inevitably failed, I’d just delete the database and
start over… which defeats the &lt;em&gt;entire&lt;&#x2F;em&gt; purpose of having a database, and
is probably why I never learned to trust them.)&lt;&#x2F;p&gt;
&lt;p&gt;Today, I am happy to report that my understanding of databases is a
little more mature. A while back I read &lt;em&gt;Designing Data Intensive Applications&lt;&#x2F;em&gt; (what a great read!). I now understand that
databases are engines for managing and querying state (no way!); I understand why relational modeling encodes real-world data so readily
and the benefits of normalization; the purpose of consistency guarantees and distributed
consensus; the need for a relational query language. (Objects are not
relations, and ORMs fundamentally &lt;em&gt;cannot&lt;&#x2F;em&gt; capture this distinction.)&lt;&#x2F;p&gt;
&lt;p&gt;It pains me that SQL is such an… &lt;em&gt;unergonomic&lt;&#x2F;em&gt; language. I can’t bring
myself around to invest the time it would take to truly master it. I
have lingering hopes that a newcomer like PRQL will become popular
before I take the plunge and learn all of SQL’s little warts. In the
meantime, however, I’ll remain partial to relational logic languages
like prolog or datomic datalog. Until PRQL usurps the old guard, you can
find me messing around with miniKanren from the comfort of my own home.&lt;&#x2F;p&gt;
&lt;p&gt;Which reminds me: datomic datalog’s tight integration with Clojure is, I
believe to be, a step in the right direction. The functional-relational
ideal has always been the inclusion of relations in the language
&lt;em&gt;itself&lt;&#x2F;em&gt;. There’s this beautiful mapping between functional &lt;em&gt;relational&lt;&#x2F;em&gt;
programming and functional &lt;em&gt;reactive&lt;&#x2F;em&gt; programming. What is reactive
programming but code that incrementally updates when underlying data
relations change?&lt;&#x2F;p&gt;
&lt;p&gt;On the web, if you’re doing reactive programming, you’re probably using
React. It is an oft-quoted mantra that UI should be a function of
application state. Managing application state is hard (…this is why we
have databases). React’s development has driven this huge
Redux-industrial complex of libraries seeking to be the one true manager of application state. As we
approach the limit, Greenspun silently pines an echo lost to time, “any
sufficiently complicated Redux cache is an ad hoc implementation of half
of Postgres.”&lt;&#x2F;p&gt;
&lt;p&gt;Greenspun, a pioneer of database-backed websites, probably groks the
irony. We have a full &lt;em&gt;relational&lt;&#x2F;em&gt; database on the backend, and a fancy
&lt;em&gt;reactive&lt;&#x2F;em&gt; UI on the frontend, and they’re synchronized by… manually
exchanging handcrafted trees of data? In a perfect world, there is no
manual synchronization: the app should be a function of database state.
In practice, however, we cache a poor denormalized copy of a database in
the browser and synchronize it through REST, or maybe GraphQL. (If you’re
lucky?) It’s 2023, &lt;em&gt;why&lt;&#x2F;em&gt;?&lt;&#x2F;p&gt;
&lt;p&gt;Synchronizing state through REST is like emptying a lake with a cup.
Open the floodgates, replicate the database client-side, and a sea of
possibilities opens up. What would websites look like if we could query
a replica of a database from the browser?&lt;&#x2F;p&gt;
&lt;p&gt;This is the amable vision of &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.inkandswitch.com&#x2F;local-first&#x2F;&quot;&gt;local first software&lt;&#x2F;a&gt;, that “everyone owns
their data, in spite of the cloud”. With this shift in perspective, we
can imagine a React app that reactively streams database updates
between the browser’s replica and other computers they own (or the cloud). (To paint a fuller
picture of this vision, see Geoffrey Litt’s &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=zjl7CpG9h3w&quot;&gt;talk on Riffle and reactive
relational state&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;These are all old ideas cast in a new light, of course. There are a
number of competing client-side data synchronizers: the most promising
solutions generally follow the pattern of wrapping a small relational
database (like SQLite) with conflict-free replicated data types (CRDTs).&lt;&#x2F;p&gt;
&lt;p&gt;I &lt;em&gt;love&lt;&#x2F;em&gt; CRDTs, but must remain clear-eyed. Having written and worked
with CRDTs, I can say that they are no silver bullet. CRDTs enforce
eventual consistency by maintaining &lt;em&gt;local invariants&lt;&#x2F;em&gt;. They guarantee
nothing about the &lt;em&gt;global validity&lt;&#x2F;em&gt; of merged states. As time progresses,
the need for “authoritative servers” will become clear: it is impossible
to enforce global invariants using CRDTs, and clients can’t be trusted
to always be honest.&lt;&#x2F;p&gt;
&lt;p&gt;This limitation leads to a nice business model for database replication
companies, I guess: keep the popular CRDT libraries open source and easy
to integrate, but charge a subscription for hosted conflict resolution
of global invariants, and long-term state storage. I’m certain you could market that a little better,
and I’m certain people will.&lt;&#x2F;p&gt;
&lt;p class=&quot;tag&quot;&gt;~540 words in 47 minutes, 11 wpm sustained&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Habits for daily writing</title>
        <published>2025-04-14T00:00:00+00:00</published>
        <updated>2025-04-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/writing/"/>
        <id>https://slightknack.dev/blog/writing/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/writing/">&lt;blockquote&gt;
&lt;h1 id=&quot;context&quot;&gt;Context&lt;&#x2F;h1&gt;
&lt;p&gt;I wrote this piece—like, let’s see, gosh where did I put it… ah yes here it is—on &lt;code&gt;2022-11-26&lt;&#x2F;code&gt;. That’s 870 days ago. For the non-walking calculators (non-calculating walkers?) among us, that’s 2 years 4 months ago.&lt;&#x2F;p&gt;
&lt;p&gt;I lived in Brazil for 2 years. In &lt;em&gt;Rio Grande do Sul&lt;&#x2F;em&gt;, what a wonderful beautiful place! Living in Brazil was a great experience, perhaps a story for another time. (One day I’ll publish that piece, I promise.) I would go into more detail about why &lt;em&gt;I&lt;&#x2F;em&gt; was in Brazil, of all places, but this blog is on the public internet, etc. (&lt;em&gt;who knows&lt;&#x2F;em&gt; who is reading this). So, until I figure out exactly what to write about Brazil, apologies for the omission.&lt;&#x2F;p&gt;
&lt;p&gt;I wrote &lt;em&gt;a lot&lt;&#x2F;em&gt; while I lived in Brazil. I think, over the course of 2 years, I wrote 300k+ words a lot (as of the last time I counted). Mainly short-form essays (like the one that follows this big context block)! The process of writing 300k words over the course of 2 years was in actuality quite interesting (or so I tell myself): In short, I kept a document called &lt;em&gt;Questions&lt;&#x2F;em&gt; that I added to whenever an interesting idea came to mind. Each morning, I would spend about 30 minutes to an hour trying to write “an answer” to the most interesting question (Which worked out to about 500 words per answer, give or take, though usually over 1,000. I often went over, ha.) That’s not all there is to it; I also wrote lengthy specifications for not one but two (!) new programming languages, &lt;em&gt;ilk&lt;&#x2F;em&gt; and &lt;em&gt;affetto&lt;&#x2F;em&gt; (Those two darlings will one day see the light of day, I promise), as well as a weekly newsletter for friends and family about my adventures.&lt;&#x2F;p&gt;
&lt;p&gt;The essay that follows is an answer to one of the first &lt;em&gt;Questions&lt;&#x2F;em&gt; I recorded. It was (let me check, ah here), “how can I build habits to write consistently?” At the time, I knew I wanted to write &lt;em&gt;daily&lt;&#x2F;em&gt;, to write about interesting and personal ideas. I also had never been a consistent writer; I wasn’t sure what the process should look like, what my goal in writing so frequently should be, and whether writing daily &lt;em&gt;was&lt;&#x2F;em&gt; something worth trying to do. (As opposed to, e.g., keeping a better journal.)&lt;&#x2F;p&gt;
&lt;p&gt;So in answering the question, I learned quite a lot. The word &lt;em&gt;essay&lt;&#x2F;em&gt; comes from the French “to try” (that’s English, no fair!). &lt;em&gt;Trying&lt;&#x2F;em&gt; is what writing is: a tool for thinking, a tool through which to try. Through writing, you make your fuzzy ideas real; they’re ugly, but they’re &lt;em&gt;real&lt;&#x2F;em&gt;, and concrete, and you can &lt;em&gt;work on them&lt;&#x2F;em&gt; and &lt;em&gt;shape them&lt;&#x2F;em&gt; until they’re better, digestible, and match the &lt;em&gt;picture&lt;&#x2F;em&gt; you were holding in your mind &lt;em&gt;this whole time&lt;&#x2F;em&gt;, which picture has now &lt;em&gt;immeasurably shifted&lt;&#x2F;em&gt; as the &lt;em&gt;concreteness&lt;&#x2F;em&gt; of the words on the page &lt;em&gt;permeate&lt;&#x2F;em&gt; your mental model of the subject, causing a temporary &lt;em&gt;convergence&lt;&#x2F;em&gt; of clarity and symmetry which provide the focus &lt;em&gt;you&lt;&#x2F;em&gt; need to figure out what “to try” to &lt;em&gt;think about&lt;&#x2F;em&gt; next. Wow. That was winded. I digress.&lt;&#x2F;p&gt;
&lt;p&gt;By writing the essay that follows, I was able to adopt quite a lot of the principles I outlined, and I begun writing daily. Above all else, the essay that follows was the spark that helped me improve drastically as a writer. This experiment, that of writing daily and maintaining a &lt;em&gt;Questions&lt;&#x2F;em&gt; document to do so, was worth it! I would do it again.&lt;&#x2F;p&gt;
&lt;p&gt;Which brings me to today. I want to do it again! I &lt;em&gt;really&lt;&#x2F;em&gt; want to start writing again. I want to get my ideas out there. I’m sitting on 300k words of original thought, and it is &lt;em&gt;killing&lt;&#x2F;em&gt; me to watch the window of relevance slip past me. I want to build habits to write consistently again. More than that, to publish consistently.&lt;&#x2F;p&gt;
&lt;p&gt;Below is the answer to the question, in full. I hope you enjoy it. In re-reading my past self’s answer, I hope to start writing daily, once again. It’s quite a lot; We’re headed into final season here at MIT, I have to wake up early tomorrow, and I have a midterm on Wednesday, but I think I can handle it.&lt;&#x2F;p&gt;
&lt;p&gt;Starting today, I hope to gradually publish the pieces I have written, starting with those I wrote over the course of my time in Brazil. I also hope to include new pieces that contextualize what I’ve written and more fully explore ideas briefly outlined.&lt;&#x2F;p&gt;
&lt;p&gt;This piece is nothing special. It’s more of an exercise in writing, a first stroke to build momemtum. Enjoy.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;question-how-can-i-build-habits-to-write-consistently&quot;&gt;Question: How can I build habits to write consistently?&lt;&#x2F;h1&gt;
&lt;p class=&quot;tag&quot;&gt;2022-11-26 · Isaac Clayton&lt;&#x2F;p&gt;
&lt;p&gt;The pair of skills that will have the greatest impact on your ability to
communicate complex ideas are, above all else, the skills of &lt;em&gt;thinking
critically&lt;&#x2F;em&gt; and &lt;em&gt;writing effectively&lt;&#x2F;em&gt;. If there’s anything we learn from
our education, let it be this, as the application of these skills will
carry you through much of our professional life. Volumes have been
written about the merits of these two skills; I do not want to repeat
that argument here.&lt;&#x2F;p&gt;
&lt;p&gt;Instead, I would like to explicitly focus on building the skill of
effective writing. Writing is the point at which the suspended structure
of thoughts we hold in our mind become ink on a page: as writers, we
must learn how to cross this boundary as seamlessly as possible. Our
goal, after all, is to recreate our pattern of thought in the reader’s
mind—keeping her engaged—lest her eyes wander off the page and the
unfinished structure we are transmitting collapses under its own weight.&lt;&#x2F;p&gt;
&lt;p&gt;As writers, we have a difficult task: we must learn how to break down
and ship structures of thought across the boundary of our mind, such
that they can be reassembled incrementally, each sentence standing on
its own. Much ink has been spilled on techniques for casting ideas in
stone (‘On Writing Well’ by Zensser comes to mind); this is not that
essay.&lt;&#x2F;p&gt;
&lt;p&gt;Learning to write well comes with time. There is no single trick, or bag
of tricks, that can substitute for content, clarity, or style. Like
anything else in life, to truly get better at writing, you must
practice. And to practice writing, you must write consistently. That is
the theme of this essay today (betrayed by the title, I suppose): How
can we, as writers, build &lt;em&gt;habits&lt;&#x2F;em&gt; to write &lt;em&gt;consistently&lt;&#x2F;em&gt;?&lt;&#x2F;p&gt;
&lt;p&gt;I would like to take a second to focus on the first part of that
question, the question of building &lt;em&gt;habits&lt;&#x2F;em&gt;. Habits are, after all, an
action we perform habitually; without much thought to whether we do it
or not. Building habits is very, very hard: after all, they’re not
usually something we think about. To build a habit, we need to change
the way we think. Building habits requires changing patterns of thought.&lt;&#x2F;p&gt;
&lt;p&gt;If our goal is to write effectively, we know that there is no better
substitute for improvement than practice: consistent, &lt;em&gt;daily&lt;&#x2F;em&gt; writing.
While a realistic, concrete goal, it’s one that seems to be awfully hard
to attain in practice. (At least for me, that is: although I write a
lot, I rarely &lt;em&gt;finish&lt;&#x2F;em&gt; a single piece daily). So what’s holding us back?&lt;&#x2F;p&gt;
&lt;p&gt;Habits are centered around patterns of thought. It’s an age-old adage
that desires become thoughts, thoughts become actions, and actions,
well, become &lt;em&gt;habits&lt;&#x2F;em&gt;. We all have the &lt;em&gt;desire&lt;&#x2F;em&gt; to become better
writers. If your eyes haven’t yet wandered off the page by this point,
you’re probably the type to spend a lot of time thinking about writing.
Despite that, we don’t &lt;em&gt;actually&lt;&#x2F;em&gt; write as often as we want. So what
patterns of thought are preventing us from performing the action of
writing daily?&lt;&#x2F;p&gt;
&lt;p&gt;For me, at least, the days when I do not write are days when I excused
myself from the task. When confronted with the thought of, “hey, you
have the goal of learning to &lt;em&gt;write effectively&lt;&#x2F;em&gt;, why don’t you write
something right now?” my mind has no shortage of answers and excuses.
Exposing a few of the more common candidates, my mind often
automatically replies:&lt;&#x2F;p&gt;
&lt;p&gt;“I don’t have any time today, but I’ll write tomorrow.”&lt;&#x2F;p&gt;
&lt;p&gt;Or:&lt;&#x2F;p&gt;
&lt;p&gt;“That sounds great, but I’m all out of great ideas. Maybe we can read
about Tropical Semirings to get some ideas!”&lt;&#x2F;p&gt;
&lt;p&gt;(Followed by a distinct &lt;em&gt;lack&lt;&#x2F;em&gt; of writing about Tropical Semirings). Or:&lt;&#x2F;p&gt;
&lt;p&gt;“I never finish anything. Why bother starting something new? Maybe we
can add to that 50-page essay outline we’ve been working on for months.”&lt;&#x2F;p&gt;
&lt;p&gt;(Spoiler Alert: that essay is a black hole of time and attention, and
will never be finished.)&lt;&#x2F;p&gt;
&lt;p&gt;In our quest to become masterful writers, such responses are often
discouraging. Well-intended or not, they direct mental energy away from
the task at hand, which is writing. So when we’re prompted to write
about something, there’s no excuse: Just Frickin’ Write!&lt;&#x2F;p&gt;
&lt;p&gt;In the interest of building the habit of writing by changing patterns of
thought, instead of being disheartened when destructive responses pop
into our minds, we must redirect our train of thought back on track.
Confronted with our above instinctive responses, we can instead chime
back:&lt;&#x2F;p&gt;
&lt;p&gt;“Even though we’re really busy today, we still have enough time to write
a little.”&lt;&#x2F;p&gt;
&lt;p&gt;Or:&lt;&#x2F;p&gt;
&lt;p&gt;“Let’s start writing a little about Tropical Semirings first; we’ll
start generating questions to write about in no time.”&lt;&#x2F;p&gt;
&lt;p&gt;(Note that our responses always redirect towards the actual action of
writing.) Or:&lt;&#x2F;p&gt;
&lt;p&gt;“How about instead, we write something small, no bigger than 200 words,
and actually &lt;em&gt;finish&lt;&#x2F;em&gt; it? If you still want to work on the essay, we can
quickly prune it into something smaller, then trim off a branch we can
easily finish &lt;em&gt;today&lt;&#x2F;em&gt;.”&lt;&#x2F;p&gt;
&lt;p&gt;Our goal here is not to prolong our mental exchange. Our goal is to
start writing. Despite our best efforts, our wills are weak and
context-dependent. Every second we spend locked in mental
conflict—instead of putting ink on page—is a second we leave ourselves
open to attacks from other distractions. We will lose the battle, unless
our pen physically hits the page, and words start flowing. The pen is
your most powerful weapon, even mightier than the sharp sword of reason,
so use it!&lt;&#x2F;p&gt;
&lt;p&gt;In our quest to communicate complex ideas with others, there are no
greater companions than those of critical thought and effective writing.
To become better at writing, we must practice: consistently writing
daily. Practice, of course, is a habit, and habits are hard to build. We
must attack the problem at the root: to build the habit of writing, just
start writing. Dissuading and distracting thoughts may pop up, trying to
pull you away from starting your work. Do not try to reason with them:
in doing so you’ll rationalize them into reasons to not write. Instead,
direct any wayward energy into the page: once you start writing, your
mind will shift gears, and it’ll be much easier to sustain your ability
to stay concentrated.&lt;&#x2F;p&gt;
&lt;p&gt;I write this essay as the first step in my journey to become a better
writer and thinker. I finally confronted the issue, thought about what
has worked for me in the past, and made a plan of action going forward.
If you are reading this someday, it is because the advice to myself
contained herein worked well enough for me to develop the habit of
consistent daily writing. In future essays I hope to document my system,
my process, and how it evolves over time. With my goal for finishing
something today met, however, that is a topic I must save for tomorrow.&lt;&#x2F;p&gt;
&lt;p&gt;So what are you waiting for? Just write!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>What&#x27;s up with Haskell&#x27;s do notation?</title>
        <published>2025-01-30T00:00:00+00:00</published>
        <updated>2025-01-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/do-notation/"/>
        <id>https://slightknack.dev/blog/do-notation/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/do-notation/">&lt;p&gt;Managing side effects in &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.haskell.org&#x2F;index.php?title=Functional_programming#Purity&quot;&gt;pure&lt;&#x2F;a&gt; functional programming
languages has &lt;a href=&quot;&#x2F;pdfs&#x2F;church-lambda.pdf&quot;&gt;always been&lt;&#x2F;a&gt; something of a challenge.
Functions in &lt;em&gt;purely&lt;&#x2F;em&gt; functional languages produce outputs
solely dependent on their inputs, by definition. Purity
makes it easy to reason about functions:
because all context is explicit, functions also become easy to break
apart and refactor.&lt;&#x2F;p&gt;
&lt;p&gt;The issue, however, with &lt;em&gt;explicit&lt;&#x2F;em&gt; context is that it quickly
becomes verbose. Unlike imperative languages, I&#x2F;O is no
longer as simple as a call to print: each function that prints
something requires a &lt;em&gt;context&lt;&#x2F;em&gt; to print it in, and must
return this context to the function that called it for later use (lest
the output be lost).&lt;&#x2F;p&gt;
&lt;p&gt;Like a game of hot potato, this I&#x2F;O context must be handed up and
down the call stack, passing through the hands of every function in between.
A task as simple as adding logging to a deep leaf function becomes an
immense &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;6310961&#x2F;how-do-i-do-logging-in-haskell#answer-6311338&quot;&gt;chore&lt;&#x2F;a&gt;, as every function that calls the
leaf function that now performs I&#x2F;O must accept and return an I&#x2F;O context.&lt;&#x2F;p&gt;
&lt;p&gt;Practical functional languages—those of the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Lisp_(programming_language)&quot;&gt;Lisp&lt;&#x2F;a&gt; and
&lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ML_(programming_language)&quot;&gt;ML families&lt;&#x2F;a&gt;—tend to take the easy way out by adding an imperative
&lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ocaml.org&#x2F;docs&#x2F;mutability-imperative-control-flow&quot;&gt;escape hatch&lt;&#x2F;a&gt;. I&#x2F;O is special-cased: every function
“implicitly” takes a global context in which to print. While practically
viable, this solution is inflexible and can get messy when there are many
types of side effects that useful programs need to perform.&lt;&#x2F;p&gt;
&lt;p&gt;Personally, I am a fan of the more composable &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;homepages.inf.ed.ac.uk&#x2F;gdp&#x2F;publications&#x2F;Effect_Handlers.pdf&quot;&gt;&lt;em&gt;Algebraic
Effects&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; approach to handling side effects.
While probably deserving an essay in their own right,
Algebraic Effects neatly unify &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.n-a-n-o.com&#x2F;lisp&#x2F;cmucl-tutorials&#x2F;LISP-tutorial-12.html&quot;&gt;dynamically scoped&lt;&#x2F;a&gt; &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;monte.readthedocs.io&#x2F;en&#x2F;latest&#x2F;intro.html#object-capability-discipline&quot;&gt;capabilities&lt;&#x2F;a&gt;
with the benefits of &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Effect_system&quot;&gt;type system&lt;&#x2F;a&gt; and inference.
While &lt;em&gt;definitionally&lt;&#x2F;em&gt; more complex than a language with
implicit side effects, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;koka-lang.github.io&#x2F;koka&#x2F;doc&#x2F;book.html#sec-effect-types&quot;&gt;in &lt;em&gt;practice&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;, Algebraic Effects are deceptively
simple. You can write code as if it were imperative.
The compiler keeps track of what effects are used where,
threading context as needed, and lowers your imperative-looking code
to something functional, pure, and easy to reason about.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;i-swear-this-is-not-a-monad-tutorial&quot;&gt;I swear this is not a monad tutorial&lt;&#x2F;h1&gt;
&lt;p&gt;Another way to manage side effects are through &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.haskell.org&#x2F;All_About_Monads&quot;&gt;&lt;em&gt;Monads&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;, as
exemplified by Haskell et … uh, just Haskell, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;lean-lang.org&#x2F;functional_programming_in_lean&#x2F;monads.html&quot;&gt;really&lt;&#x2F;a&gt;. Monads describe a &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;wiki.haskell.org&#x2F;Type_class&quot;&gt;class of
types&lt;&#x2F;a&gt; with associated properties that generally make them
amenable to modeling side effects.&lt;&#x2F;p&gt;
&lt;p&gt;This is not a monad tutorial, so with complete lack of tact, I’d
like to restate that a Monad is, in general, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Monad_(functional_programming)#More_examples&quot;&gt;anything that is
thenable&lt;&#x2F;a&gt;. Semantically, Monads are quite simple: A Monad is a class
of types where the following three operations are available, and
obey the so-called &lt;em&gt;Monad Laws&lt;&#x2F;em&gt; (which I describe later):&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Return&lt;&#x2F;em&gt;, which wraps a value in the default context.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Then (&lt;code&gt;&amp;gt;&amp;gt;&lt;&#x2F;code&gt;)&lt;&#x2F;em&gt;, which takes two contexts, and merges them
together as if one happened after the other.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Bind (&lt;code&gt;&amp;gt;&amp;gt;=&lt;&#x2F;code&gt;)&lt;&#x2F;em&gt;, which takes a value in a context, applies a
transformation to that value, and produces a new context.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;In Haskell we’d define a Monad as:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Monad m &lt;&#x2F;span&gt;&lt;span&gt;where
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;:: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;m a
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;(&amp;gt;&amp;gt;) &lt;&#x2F;span&gt;&lt;span&gt;:: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;m a &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;m b &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;m b
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;(&amp;gt;&amp;gt;=) &lt;&#x2F;span&gt;&lt;span&gt;:: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;m a &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;m b&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;m b
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that, in Haskell, both &lt;em&gt;then&lt;&#x2F;em&gt; (&lt;code&gt;&amp;gt;&amp;gt;&lt;&#x2F;code&gt;) and &lt;em&gt;bind&lt;&#x2F;em&gt;
(&lt;code&gt;&amp;gt;&amp;gt;=&lt;&#x2F;code&gt;) are defined as infix operators.&lt;&#x2F;p&gt;
&lt;p&gt;There are many different instances of Monads that exist in the wild. A
relatively simple Monad is &lt;code&gt;Maybe&lt;&#x2F;code&gt;, whose context is whether
or not a value exists. The implementation is not too complex:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;-- The value exists or it does not.
&lt;&#x2F;span&gt;&lt;span&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Maybe&lt;&#x2F;span&gt;&lt;span&gt; a = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just&lt;&#x2F;span&gt;&lt;span&gt; a | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Nothing
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;instance &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Monad Maybe &lt;&#x2F;span&gt;&lt;span&gt;where
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;-- By default the value exists.
&lt;&#x2F;span&gt;&lt;span&gt;  return a = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just&lt;&#x2F;span&gt;&lt;span&gt; a
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;-- Preserve context and replace value.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;(&amp;gt;&amp;gt;)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just&lt;&#x2F;span&gt;&lt;span&gt; a) b = b
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;(&amp;gt;&amp;gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Nothing&lt;&#x2F;span&gt;&lt;span&gt; _ = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Nothing
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;-- Can only transform existing values.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;(&amp;gt;&amp;gt;=)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just&lt;&#x2F;span&gt;&lt;span&gt; a) f = f a
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;(&amp;gt;&amp;gt;=) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Nothing&lt;&#x2F;span&gt;&lt;span&gt; _ = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Nothing
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And here’s how you would use return, then (&lt;code&gt;&amp;gt;&amp;gt;&lt;&#x2F;code&gt;), and bind
(&lt;code&gt;&amp;gt;&amp;gt;=&lt;&#x2F;code&gt;) with the Maybe Monad:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;7 &lt;&#x2F;span&gt;&lt;span&gt;:: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Maybe
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;-- Just 7
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Hello&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Nothing &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Bye&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;-- Nothing
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;7 &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just &lt;&#x2F;span&gt;&lt;span&gt;. &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;(+) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;-- Just 9
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These allow us to chain together monadic operations.
Note that in the last example, the context wrapping the value &lt;code&gt;7&lt;&#x2F;code&gt; is
preserved (e.g. we get &lt;code&gt;Just 9&lt;&#x2F;code&gt;). If we were to use
&lt;code&gt;Nothing&lt;&#x2F;code&gt; instead, we would get &lt;code&gt;Nothing&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Nothing &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just &lt;&#x2F;span&gt;&lt;span&gt;. &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;(+) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;-- Nothing
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Because we can’t &lt;em&gt;add two&lt;&#x2F;em&gt; to a value we don’t know!&lt;&#x2F;p&gt;
&lt;p&gt;Now the infix operator syntax for chaining monads is nice,
especially when writing point-free code, because we can define
these monadic transformations a bit like steps in a pipeline:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;get &lt;&#x2F;span&gt;&lt;span&gt;:: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;AddressBook &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Maybe String
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;parse_email &lt;&#x2F;span&gt;&lt;span&gt;:: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Maybe Email
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;send_email &lt;&#x2F;span&gt;&lt;span&gt;:: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Email &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Maybe Thread
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;get contacts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Euclid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;= parse_email
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;= send_email message
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This example is short and reads well, because &lt;code&gt;parse_email&lt;&#x2F;code&gt; and
&lt;code&gt;send_email message&lt;&#x2F;code&gt; are functions with the exact type
signatures we expect at each stage in the pipeline.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;points-and-legos&quot;&gt;Points and legos&lt;&#x2F;h1&gt;
&lt;p&gt;Sometimes, however, the lego bricks in our pipeline don’t quite lock
together, as we have to adopt a style that uses &lt;em&gt;points&lt;&#x2F;em&gt;. In a
point-ful style, we use explicit anonymous functions (i.e.
&lt;em&gt;lambdas&lt;&#x2F;em&gt;) to pipe values together. I like to remember that the
arrow in a lambda is &lt;em&gt;pointy&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;add_two &lt;&#x2F;span&gt;&lt;span&gt;:: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Int &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Int
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;-- Point-free style
&lt;&#x2F;span&gt;&lt;span&gt;add_two = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;(+) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;-- Pointed style
&lt;&#x2F;span&gt;&lt;span&gt;add_two = \n -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;+ n
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To demonstrate the increased verbosity of using explicit points with
Monadic operations, here is the previous email snippet rewritten in a
point-ful style, using lambdas:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span&gt;get contacts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Euclid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;= (\raw_email -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  parse_email raw_email
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;gt;&amp;gt;= (\email -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    send_email message email))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which is, admittedly, quite a lot worse than:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span&gt;get contacts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Euclid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;= parse_email
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;= send_email message
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Although Haskell is generally pretty flexible, and provides plenty
of tools for wrangling pointed expressions into their equivalent
point-free forms, there are times where a pointed style is
simpler to understand than the convoluted currying and type
wrangling that may be required to enforce a strict
point-free style.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;do-notation-at-last&quot;&gt;Do notation at last&lt;&#x2F;h1&gt;
&lt;p&gt;Enter, Haskell’s &lt;em&gt;do notation&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Haskell’s do notation is a compact notation for writing monadic
pipelines: it is powerful syntactic sugar that helps make
composing Monads easier. Here is the previous email snippet
written as a &lt;code&gt;do&lt;&#x2F;code&gt; expression:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  raw_email &amp;lt;- get contacts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Euclid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  email &amp;lt;- parse_email raw_email
&lt;&#x2F;span&gt;&lt;span&gt;  send_email message email
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which almost reads like imperative code. (This surface-level
similarity to straight-line code can be a pitfall for
beginners, but more on that later.)&lt;&#x2F;p&gt;
&lt;p&gt;So how does do notation work?&lt;&#x2F;p&gt;
&lt;p&gt;Do notation is syntactic sugar for the standard then (&lt;code&gt;&amp;gt;&amp;gt;&lt;&#x2F;code&gt;) and bind
(&lt;code&gt;&amp;gt;&amp;gt;=&lt;&#x2F;code&gt;) operators. Each line in a &lt;code&gt;do&lt;&#x2F;code&gt; block is chained with the next using
a monadic operator.&lt;&#x2F;p&gt;
&lt;p&gt;To clarify, let’s look at a simple case with two lines. When we have two
simple expressions, one after another, like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Nothing
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Hello&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This &lt;code&gt;do&lt;&#x2F;code&gt; expression will desugar to the then (&lt;code&gt;&amp;gt;&amp;gt;&lt;&#x2F;code&gt;) operator:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Nothing &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Hello&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which is &lt;code&gt;Nothing&lt;&#x2F;code&gt;.
When an line yields a wrapped monadic value, we can use &lt;code&gt;&amp;lt;-&lt;&#x2F;code&gt; to
extract the value inside the Monad for use in the rest of the
expression:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  seven &amp;lt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;7
&lt;&#x2F;span&gt;&lt;span&gt;  return (seven + &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This &lt;code&gt;&amp;lt;-&lt;&#x2F;code&gt; desugars to the bind (&lt;code&gt;&amp;gt;&amp;gt;=&lt;&#x2F;code&gt;) operator and a lambda as
follows:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#7697d6;&quot;&gt;Just &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;7 &lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;= (\seven -&amp;gt; return (seven + &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that an implicit lambda was introduced, wrapping the rest
of the &lt;code&gt;do&lt;&#x2F;code&gt; expression. This is where the power of the &lt;code&gt;do&lt;&#x2F;code&gt;
expression lies: it allows us to express multiple pointed binds as a
straight-line series of expressions, which &lt;em&gt;eliminates nesting&lt;&#x2F;em&gt; and
becomes easier to read. The context of the previous expression is
transparently carried forward to the next, meaning we don’t have
to write out deeply-nested callbacks. Do notation slices apart
nested monadic transformations at the joints.&lt;&#x2F;p&gt;
&lt;p&gt;Haskell’s do notation is deeply related to &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;koka-lang.github.io&#x2F;koka&#x2F;doc&#x2F;book.html#sec-with&quot;&gt;&lt;em&gt;with notation&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;
in Koka (which does for Algebraic Effects what do
notation does for Monads) and &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gleam.run&#x2F;news&#x2F;v0.25-introducing-use-expressions&#x2F;&quot;&gt;&lt;em&gt;use notation&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; in
Gleam. How these map to Haskell’s do notation (via the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;siraben.dev&#x2F;2020&#x2F;02&#x2F;20&#x2F;free-monads.html&quot;&gt;&lt;em&gt;Free Monad&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;) will perhaps be
the topic of another post.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;when-do-notation-doesn-t-run-sequentially&quot;&gt;When do notation doesn’t… run sequentially&lt;&#x2F;h1&gt;
&lt;p&gt;There are, however, a couple of pitfalls, which trap those new
to Haskell. On the surface, do notation looks similar to
imperative code: people coming from imperative languages
gravitate towards using &lt;code&gt;do&lt;&#x2F;code&gt; in simple cases where an idiomatic
point-free style is more appropriate. Using &lt;code&gt;do&lt;&#x2F;code&gt; can
needlessly complicate simple code:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  line &amp;lt;- get_line ()
&lt;&#x2F;span&gt;&lt;span&gt;  return line
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;While ostensibly sensible, this do block becomes:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span&gt;get_line () &amp;gt;&amp;gt; (\line -&amp;gt; return line)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which is equivalent to:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;get_line ()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is a lot shorter (and point free)!&lt;&#x2F;p&gt;
&lt;p&gt;The second pitfall beginners face is not ever learning about the Monadic
operations that underlie do notation. (Which is a pitfall I hope I have
addressed in this post.)&lt;&#x2F;p&gt;
&lt;p&gt;To elaborate, do notation is usually used as a shorthand in
contexts that require I&#x2F;O. This tight coupling in
presentation may cause beginners to think that do notation is just
‘how one does’ imperative-style I&#x2F;O in Haskell. In reality, do notation
is a much more powerful tool: it can handle &lt;em&gt;any&lt;&#x2F;em&gt; Monad, not
just the IO Monad.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, thinking of do notation as an ‘imperative escape hatch’ is
also incorrect and has its pitfalls. Consider the following snippet:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;get_line &lt;&#x2F;span&gt;&lt;span&gt;:: () -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;IO String
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;print_line &lt;&#x2F;span&gt;&lt;span&gt;:: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;IO &lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;friend = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  name &amp;lt;- get_line ()
&lt;&#x2F;span&gt;&lt;span&gt;  print_line (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Hello, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;++ name)
&lt;&#x2F;span&gt;&lt;span&gt;  return name
&lt;&#x2F;span&gt;&lt;span&gt;  print_line &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Unreachable&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Casper the Ghost&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let’s say you run this snippet, type &lt;code&gt;James&lt;&#x2F;code&gt; and hit enter. What is
printed, and who ends up as your &lt;code&gt;friend&lt;&#x2F;code&gt;?&lt;&#x2F;p&gt;
&lt;p&gt;Adopting an imperative lens, one might think:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;First, we store &lt;code&gt;&quot;James&quot;&lt;&#x2F;code&gt; in the &lt;code&gt;name&lt;&#x2F;code&gt; variable. Next, we print
&lt;code&gt;Hello, James&lt;&#x2F;code&gt;. We then encounter &lt;code&gt;return name&lt;&#x2F;code&gt; right in the middle
of our &lt;code&gt;do&lt;&#x2F;code&gt; block: since &lt;code&gt;return&lt;&#x2F;code&gt; short circuits control flow in
most other languages, it must certainly do the same in Haskell, so our
&lt;code&gt;friend&lt;&#x2F;code&gt; would be &lt;code&gt;&quot;James&quot;&lt;&#x2F;code&gt;, and the only output we should see would
be that of the first print statement…&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Right?&lt;&#x2F;p&gt;
&lt;p&gt;Wrong.&lt;&#x2F;p&gt;
&lt;p&gt;We’d actually end up with &lt;code&gt;friend = &quot;Casper the Ghost&quot;&lt;&#x2F;code&gt;, and we would
see the following as output:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Hello, James
&lt;&#x2F;span&gt;&lt;span&gt;Unreachable
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Why?&lt;&#x2F;p&gt;
&lt;p&gt;Do notation is just sugar for chaining Monads: it is not
&lt;em&gt;actually&lt;&#x2F;em&gt; imperative code. Unlike other languages, &lt;code&gt;return&lt;&#x2F;code&gt; does
&lt;em&gt;not&lt;&#x2F;em&gt; short-circuit control flow: &lt;code&gt;return&lt;&#x2F;code&gt; is a normal
function like any other:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;:: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;m a
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the case of &lt;code&gt;IO&lt;&#x2F;code&gt;, it just wraps a string in an I&#x2F;O context,
creating an &lt;code&gt;IO String&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;io_string &lt;&#x2F;span&gt;&lt;span&gt;:: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;IO String
&lt;&#x2F;span&gt;&lt;span&gt;return = io_string
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With these definitions in place, we could desugar the above example as
follows:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span&gt;friend = get_line ()
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;gt;&amp;gt;= (\name -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    print_line (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Hello, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;++ name)
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;gt;&amp;gt; io_string name
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;gt;&amp;gt; print_line &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Unreachable&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;gt;&amp;gt; io_string &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Casper the Ghost&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see, &lt;code&gt;return name&lt;&#x2F;code&gt; in the middle reduces to creating an
&lt;code&gt;IO String&lt;&#x2F;code&gt; that is immediately discarded by the following
Monadic then (&lt;code&gt;&amp;gt;&amp;gt;&lt;&#x2F;code&gt;). This is very important:&lt;&#x2F;p&gt;
&lt;p&gt;Do notation does &lt;em&gt;not&lt;&#x2F;em&gt; imply sequential imperative
evaluation. (This is especially true because Haskell is
&lt;em&gt;lazy&lt;&#x2F;em&gt;.)&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-monad-laws-at-last&quot;&gt;The Monad Laws at last!&lt;&#x2F;h1&gt;
&lt;p&gt;With that it mind, I can finally motivate an
aesthetic presentation of the three &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.haskell.org&#x2F;Monad_laws&quot;&gt;Monad Laws&lt;&#x2F;a&gt; using do-notation.
These are rules that then (&lt;code&gt;&amp;gt;&amp;gt;&lt;&#x2F;code&gt;), bind (&lt;code&gt;&amp;gt;&amp;gt;=&lt;&#x2F;code&gt;), and return must follow
for an instance of the &lt;code&gt;Monad&lt;&#x2F;code&gt; typeclass to &lt;em&gt;actually&lt;&#x2F;em&gt; be a monad, mathematically.
(If you break the Monad Laws, the &lt;em&gt;Monad Police&lt;&#x2F;em&gt; will
show up and throw you in &lt;em&gt;Monad Jail&lt;&#x2F;em&gt; where you will labor
on the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.haskell.org&#x2F;All_About_Monads#A_physical_analogy_for_monads&quot;&gt;&lt;em&gt;Monad Assembly Line&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; until all values lost
have been bound then returned to their former state of &lt;em&gt;purity&lt;&#x2F;em&gt;. Ahem.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;left-identity&quot;&gt;Left identity&lt;&#x2F;h2&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;This:&lt;&#x2F;th&gt;
&lt;th&gt;becomes:&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  y &amp;lt;- return x
&lt;&#x2F;span&gt;&lt;span&gt;  f y
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;td&gt;
&lt;td&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  f x
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;td&gt;
&lt;&#x2F;table&gt;
&lt;h2 id=&quot;right-identity&quot;&gt;Right identity&lt;&#x2F;h2&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;This:&lt;&#x2F;th&gt;
&lt;th&gt;becomes:&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  x &amp;lt;- m
&lt;&#x2F;span&gt;&lt;span&gt;  return x
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;td&gt;
&lt;td&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  m
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;td&gt;
&lt;&#x2F;table&gt;
&lt;h1 id=&quot;associativity&quot;&gt;Associativity&lt;&#x2F;h1&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;This:&lt;&#x2F;th&gt;
&lt;th&gt;or:&lt;&#x2F;th&gt;
&lt;th&gt;becomes:&lt;&#x2F;th&gt;
&lt;&#x2F;tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  y &amp;lt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    x &amp;lt;- m
&lt;&#x2F;span&gt;&lt;span&gt;    f x
&lt;&#x2F;span&gt;&lt;span&gt;  g y
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;td&gt;
&lt;td&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  x &amp;lt;- m
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    y &amp;lt;- f x
&lt;&#x2F;span&gt;&lt;span&gt;    g y
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;td&gt;
&lt;td&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  x &amp;lt;- m
&lt;&#x2F;span&gt;&lt;span&gt;  y &amp;lt;- f x
&lt;&#x2F;span&gt;&lt;span&gt;  g y
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;td&gt;
&lt;&#x2F;table&gt;
&lt;h1 id=&quot;what-s-to-do-has-been-done&quot;&gt;What’s to do has been done&lt;&#x2F;h1&gt;
&lt;p&gt;So, to recap:&lt;&#x2F;p&gt;
&lt;p&gt;Side effects in pure functional languages
require propagating context, which results in
verbose code that is brittle to change. Haskell
circumvents this issue with Monads, which are a general way to
wrap a value in a context, and chain transformations on
a value within a given context.&lt;&#x2F;p&gt;
&lt;p&gt;However, operations on Monads—when written in a pointed
style—can quickly become verbose and nested. Do
notation is a simple syntax for flattening complex
chains of operations, offering a number of advantages over the
traditional then (&lt;code&gt;&amp;gt;&amp;gt;&lt;&#x2F;code&gt;) and bind (&lt;code&gt;&amp;gt;&amp;gt;=&lt;&#x2F;code&gt;) syntax.&lt;&#x2F;p&gt;
&lt;p&gt;Those new to Haskell coming from imperative languages
often misinterpret the core calculus of do notation and use it
overzealously. However, by knowing the Monadic operations that
underlie do notation, one can learn when to use it to drastically
simplify code.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-up-with-haskell-s-do-notation&quot;&gt;What’s up with Haskell’s do notation?&lt;&#x2F;h2&gt;
&lt;p&gt;So you’ve read a lot about do notation but
I still haven’t explained &lt;em&gt;what the big deal is&lt;&#x2F;em&gt;.
Apologies. Talk about burying the lede.&lt;&#x2F;p&gt;
&lt;p&gt;Human languages, like English, are typically read in a linear sequence, from beginning to end.
Like programming languages, human languages map to &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Parse_tree#Nomenclature&quot;&gt;parse trees&lt;&#x2F;a&gt;, according to a
grammar—or set of rules—and we can ascribe semantic &lt;em&gt;meaning&lt;&#x2F;em&gt; to those trees.&lt;&#x2F;p&gt;
&lt;p&gt;Most human languages favor parse trees that &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Branching_(linguistics)#Full_trees&quot;&gt;branch&lt;&#x2F;a&gt; in a particular direction.
English, for example, is primarily a &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Right-branching_sentences_in_English&quot;&gt;right-branching&lt;&#x2F;a&gt; language,
meaning that right-branching sentences are more common, due to the grammar of the language favoring their construction.
A right-branching sentence starts with a &lt;em&gt;subject&lt;&#x2F;em&gt; and is followed by a sequence of modifiers
that progressively add more information about the subject. To borrow &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;upload.wikimedia.org&#x2F;wikipedia&#x2F;commons&#x2F;6&#x2F;66&#x2F;Branching6.jpg&quot;&gt;an image&lt;&#x2F;a&gt; from Wikipedia,
after a certain point in a sentence, all subsequent nodes branch right:&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;content&#x2F;branching.svg&quot; alt=&quot;A parse tree for the sentence: &#x27;The child did not try to eat anything&#x27;. From &#x27;did&#x27; onward, the tree grows down and to the right.&quot;&gt;
&lt;p&gt;Speakers of English are pretty good at processing deeply-nested trees that branch to the right.
Reading right-branching sentences doesn’t feel like parsing some complicated grammatical structure.
The nesting, while deep, is simple: we always branch to the right.
Because the nesting is simple, we can treat the tree almost as a linear sequence:
we can ignore the nesting, because it is trivial.
I feel like humans are pretty good at communicating ideas by starting with a subject
and progressively adding information,
as opposed to incrementally constructing some sort of
complex tree structure the mind, which is then evaluated.&lt;&#x2F;p&gt;
&lt;p&gt;How is this related to do notation?&lt;&#x2F;p&gt;
&lt;p&gt;Haskell is also a language, and it also maps to parse trees.
Parse trees in Haskell, like in English, can lean to the left or to the right.
And when chaining operations together, like with Monads,
the parse trees can tend to lean pretty far in one direction.
Consider, for example, our pointed email-parsing example from earlier:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span&gt;get contacts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Euclid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;= (\raw_email -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  parse_email raw_email
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;gt;&amp;gt;= (\email -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    send_email message email))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In case it’s not visible from the intendation, this is a right-branching parse tree!&lt;&#x2F;p&gt;
&lt;p&gt;When we rewrite this expression using do-notation, the code is flattened:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haskell&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-haskell &quot;&gt;&lt;code class=&quot;language-haskell&quot; data-lang=&quot;haskell&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  raw_email &amp;lt;- get contacts &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Euclid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  email &amp;lt;- parse_email raw_email
&lt;&#x2F;span&gt;&lt;span&gt;  send_email message email
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is a lot easier to read,
because humans are pretty good at communicating ideas by starting with a subject
and progressively adding information.
The nesting, of course, is still there,
but the &lt;code&gt;do&lt;&#x2F;code&gt; keyword keys us in that the parse tree will be leaning to the right.
The nesting, while deep, is simple, so we can ignore it.
If Monads are context, then each line is information added to that context.
By eliminating nesting, it becomes easier to communicate hard ideas.&lt;&#x2F;p&gt;
&lt;p&gt;That’s what’s up with Haskell’s do notation.&lt;&#x2F;p&gt;
&lt;p&gt;We see this flattening a lot, in the space of programming languages.
A classic example is method call syntax, going from &lt;code&gt;foo(bar(baz))&lt;&#x2F;code&gt; to &lt;code&gt;baz.bar().foo()&lt;&#x2F;code&gt;,
which makes it easier to flatten a chain function calls, eliminating nesting along the way.
And in imperative languages, the sequencing of statements with &lt;code&gt;;&lt;&#x2F;code&gt;
can be seen as a kind of flattening composition in of itself.
Notational tweaks like these, while seemingly simple,
can make it a lot easier to express hard programs,
and thus solve hard problems.&lt;&#x2F;p&gt;
&lt;p&gt;That’s all for today. In a future post, I hope to explore how Koka’s
&lt;em&gt;with&lt;&#x2F;em&gt; &lt;em&gt;notation&lt;&#x2F;em&gt; is a variation of Haskell’s do notation
specialized for modeling Algebraic Effects using the Free
Monad. Until next time!&lt;&#x2F;p&gt;
&lt;div class=&quot;boxed&quot;&gt;
&lt;p&gt;Thank you to my friend &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.uzpg.me&#x2F;&quot;&gt;Uzay&lt;&#x2F;a&gt; for reviewing an earlier draft of this post!
(It has been sitting on my hard drive for way to long.)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Reasoning Trace</title>
        <published>2025-01-28T00:00:00+00:00</published>
        <updated>2025-01-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/reasoning-trace/"/>
        <id>https://slightknack.dev/blog/reasoning-trace/</id>
        
        <summary type="html">&lt;p&gt;&lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2501.12948&quot;&gt;DeepSeek-R1-Zero is cool&lt;&#x2F;a&gt;. I &lt;a href=&quot;&#x2F;blog&#x2F;constraints&quot;&gt;wrote about reasoning models&lt;&#x2F;a&gt; before o1, and I’m excited to the way this area of research has been cracked wide open, it seems. It’s also remarkably simple. I’m messing around with llama (running locally!), trying to see if I can at least partially reproduce the results (for fun). I figure I can collect reasoning chains and then adapt some existing RLHF code to fine-tune the model on successful chains vs. unsuccessful chains, maybe by prefixing the response with a reward token, a la &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;pdf&#x2F;2106.01345&quot;&gt;decision transformer&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;N.B.&lt;&#x2F;em&gt; I’m doing something a little more complicated where the model is allowed to write and run python programs to arrive at an answer. I’m testing on some &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;projecteuler.net&#x2F;archives&quot;&gt;&lt;em&gt;Project Euler&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; questions, because I think they are a fun blend of a mathematical and computational challenge.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It turns out that &lt;code&gt;llama-3.2-3b-instruct&lt;&#x2F;code&gt; is pretty bad at &lt;em&gt;Project Euler&lt;&#x2F;em&gt;. I’ve tested it on the first &lt;s&gt;77&lt;&#x2F;s&gt; 109 questions, and it’s gotten about 12 right. About half the time (43 cases), it gets stuck in a reasoning loop and never produces an answer (like generating Python code that isn’t properly indented and then trying everything to debug the syntax error &lt;em&gt;except indenting the code&lt;&#x2F;em&gt;). Sometimes this makes llama frustrated:&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>I&#x27;m offline, and that&#x27;s OK</title>
        <published>2025-01-22T00:00:00+00:00</published>
        <updated>2025-01-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/offline-ok/"/>
        <id>https://slightknack.dev/blog/offline-ok/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/offline-ok/">&lt;p&gt;&lt;em&gt;Stop showing me spinners, show me what’s loading.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Recently, while on a trip between cities, we hit a patch of road without
good coverage. While on long trips, I enjoy reading books I have saved
on my phone. I have some books on my phone that exist as PDFs stored on
Google Drive.&lt;&#x2F;p&gt;
&lt;p&gt;Before the trip, I started reading a few books while online, and I know
they’re downloaded. Even so, while trying to navigate the hierarchy
folders to reach said books, I was met by spinner after spinner.
Sometimes a spinner appeared because I was offline, and Drive was trying
to fetch the contents of the Drive over the network. Other times, the
spinner spun for a few seconds while loading the contents of the folder
from disk. The problem is that there is absolutely no way to tell the
difference between the two. I know I’m offline: stop showing me
spinners¹, tell me what’s loading.&lt;&#x2F;p&gt;
&lt;p&gt;When a user is offline, experience should degrade gracefully. This does
not mean that the worse the connection is the worse the experience
becomes. Rather, the offline experience should be as thought out as the
online experience, despite the degraded connection. What differentiates
incredible UX from good UX is consistently meeting user expectations,
&lt;em&gt;especially&lt;&#x2F;em&gt; when things go wrong…&lt;&#x2F;p&gt;
&lt;p&gt;…“You are offline, the contents of this folder will load once back
online.” Is as easy as a spinner, and much more informative.&lt;&#x2F;p&gt;
&lt;p&gt;…If data is cached locally, show that data, even if incomplete, and tell
me what’s loading, or needs to load. Reassure me that it’s on the
device, and it will load.&lt;&#x2F;p&gt;
&lt;p&gt;…Whenever there is not a bounded guarantee on how long something will
take to load (e.g. when reading anything from disk or over a network),
show some meaningful indication of progress. This includes updating the
UI non-destructively as data loads in (don’t shift stuff around),
showing a loading bar with uniform progress, and informing the user what
work is being done in a humane manner.&lt;&#x2F;p&gt;
&lt;p&gt;…When loading data or performing an operation with non-uniform progress
(e.g. over a network), tell me why the work is progressing slowly or
quickly. For example, one could keep a persistent indicator next to a
loading bar telling me when my connection is poor, when it is fast, when
the connection has dropped and is being reestablished, and when a
connection could not be established at all.&lt;&#x2F;p&gt;
&lt;p&gt;Figuring out how to incorporate these elements into a modern UI is a
hard problem, but one that has already mostly been solved.&lt;&#x2F;p&gt;
&lt;p&gt;The biggest issue, most likely, is economical: justifying the added cost
of having offline support. This is a false dichotomy: by default, all
applications are offline, and a network connection enhances this default
behaviour. Offline support is not an addition, but a foundation: if your
app is architected to work well when offline, it will be more robust
when progressively enhanced with internet connectivity.&lt;&#x2F;p&gt;
&lt;p&gt;Building robust apps is important: users will not keep coming back to an
app that is broken, no matter how useful it is when it works. Users may
be offline at times. Don’t make your app the point of a user’s
frustration: an offline experience shouldn’t be a broken one.&lt;&#x2F;p&gt;
&lt;p&gt;At the end of the day, whether one is “connected to the Internet” or not
is not a question with a binary answer. A connection may be perfectly
fine for texting, yet not hold up when said conversation moves to a
video call. A high-latency, yet high-throughput connection might work
great while streaming, but terribly while gaming online. What is being
offline but having a connection with extremely high latency and
extremely low throughput?&lt;&#x2F;p&gt;
&lt;p&gt;Internet connectivity depends on the state of the physical
infrastructure of the internet. This will never be something application
authors—not even Google—will have complete control over. Even if strong
connectivity is guaranteed, consider that users may want to use your app
offline. The default is no connectivity, enhanced by a network
connection. Applications that are built to be local-first are more
robust at an architectural level, and lend themselves to developing an
incredible user experience with greater ease.&lt;&#x2F;p&gt;
&lt;p&gt;I’m offline, and that’s OK. And even if I’m not, stop showing me
spinners: show me what’s loading.&lt;&#x2F;p&gt;
&lt;p&gt;—&lt;&#x2F;p&gt;
&lt;p&gt;1. Spinners originally existed to show that an app was doing work in
the background, and that the UI hadn’t frozen. In the era of
asynchronous UI updates and multithreaded code, the UI should never
freeze, especially not because of a network request. The spinner,
therefore, is a redundant artifact from a bygone era. Stop using
spinners.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>One day with Zig, Raylib, and jj</title>
        <published>2024-12-25T00:00:00+00:00</published>
        <updated>2024-12-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/zig-raylib/"/>
        <id>https://slightknack.dev/blog/zig-raylib/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/zig-raylib/">&lt;h1 id=&quot;merry-christmas&quot;&gt;Merry Christmas!&lt;&#x2F;h1&gt;
&lt;p&gt;Back from the mission, first semester at MIT is in the books! Now I am at home, with family, on a break from school.&lt;&#x2F;p&gt;
&lt;p&gt;A couple days ago, I was telling my younger brother how cool Zig (the programming language) was. He was like, “if Zig is so cool, why don’t you … like, use it?” Oof. So I &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ziglang.org&#x2F;learn&#x2F;getting-started&#x2F;#managers&quot;&gt;installed Zig&lt;&#x2F;a&gt;, pulled in &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Not-Nik&#x2F;raylib-zig&quot;&gt;some neat bindings for raylib&lt;&#x2F;a&gt;, and spent the afternoon writing a little interactive scrabble board demo to make sure that I understood what I was talking about (while he worked on some music for it, which I haven’t yet included):&lt;&#x2F;p&gt;
&lt;iframe src=&quot;&#x2F;scrabble&quot; width=&quot;100%&quot; height=&quot;600px&quot; frameborder=&quot;0&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;So that I can stop worrying about this project and lay it to rest, I decided to write a little blog post. The above demo doesn’t really work on mobile, and it may be broken (Wasm, JS, about 1MB in size, etc.), so here’s a screenshot:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;content&#x2F;scrabble-devlog.png&quot; alt=&quot;Screenshot of a scrabble board in a window on top of a Zed editor.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When I showed my brother the demo, he was like, “that’s cool beta, but where’s the game?”&lt;&#x2F;p&gt;
&lt;p&gt;You can’t win every battle.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;N.B. “Maybe if you made it a game you could” — my brother&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Anyway, the code &lt;del&gt;will be&lt;&#x2F;del&gt; &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;scrabble&quot;&gt;is now up on GitHub&lt;&#x2F;a&gt;, it’s like &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;scrabble&#x2F;blob&#x2F;master&#x2F;src&#x2F;main.zig&quot;&gt;~500 lines&lt;&#x2F;a&gt; and has &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Not-Nik&#x2F;raylib-zig&quot;&gt;like one dependency&lt;&#x2F;a&gt; (&lt;code&gt;raylib-zig&lt;&#x2F;code&gt;) so it shouldn’t be too hard to get the native build working if you’d like to follow along then. The web build is a little hacky and left as an exercise to the reader.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;why-zig&quot;&gt;Why Zig&lt;&#x2F;h1&gt;
&lt;p&gt;I have been eyeballing Zig for a while. I think I first heard of the language via a talk Andrew Kelly gave at the recurse center … ah yep here it is: &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;Z4oYSByyRak?t=157&quot;&gt;&lt;em&gt;Software Should Be Perfect&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;. 6 years ago, wow.&lt;&#x2F;p&gt;
&lt;p&gt;I really vibe with the language. From a language design PoV, generics through &lt;code&gt;comptime&lt;&#x2F;code&gt; functions is pretty fun. “Compiler as an interpreter over the static elements of the program” and all that. Also, I think @matklad has mentioned that there’s this goal of making Zig an incremental “real-time” compiler. Incrementally compiling code at 60 fps! Now that’s a goal I can get behind! From a tooling PoV, also very cool: I love the cross-compilation, and &lt;code&gt;build.zig&lt;&#x2F;code&gt;, while a bit to absorb all at once, is very useful and powerful, especially for e.g. embedding a C library like raylib.&lt;&#x2F;p&gt;
&lt;p&gt;I also came across the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tigerbeetle&#x2F;tigerbeetle&#x2F;blob&#x2F;main&#x2F;docs&#x2F;TIGER_STYLE.md&quot;&gt;TigerStyle document&lt;&#x2F;a&gt; out of TigerBeetle and it has changed the way I think about code. This project was for fun, but I can see how Zig can help scale the ideas in this document. It doesn’t try to hide anything from you. And like, aesthetically, I find the idea of e.g. statically allocating all memory up front to be very appealing.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;walk-me-through-the-code&quot;&gt;Walk me through the code&lt;&#x2F;h1&gt;
&lt;p&gt;I put &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;slightknack&#x2F;scrabble&quot;&gt;the code up on GitHub&lt;&#x2F;a&gt;, and I thought it would be fun to walk through some of it and point out some interesting stuff as we go along. Clone if you want to follow along!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;N.B. I used &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jj-vcs&#x2F;jj&quot;&gt;jujutsu (jj)&lt;&#x2F;a&gt; to do version control instead of git (without colocating) so I am figuring out whether I try to convert the jj repo to a git repo or just &lt;code&gt;git init&lt;&#x2F;code&gt; and &lt;code&gt;push&lt;&#x2F;code&gt; without any history. I’ll read the jj docs, there’s probably an easy way to export&#x2F;convert&#x2F;colocate.&lt;&#x2F;p&gt;
&lt;p&gt;Update: Steve Klabnik, the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;stable&#x2F;book&#x2F;&quot;&gt;Rust book guy&lt;&#x2F;a&gt; (and now I guess also the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;steveklabnik.github.io&#x2F;jujutsu-tutorial&#x2F;introduction&#x2F;introduction.html&quot;&gt;jj tutorial guy&lt;&#x2F;a&gt;?) &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;lobste.rs&#x2F;s&#x2F;1wbfuj&#x2F;one_day_with_zig_raylib_jj#c_irzgui&quot;&gt;pointed out on Lobsters&lt;&#x2F;a&gt; that, because jj repos are backed by git repos, you can just &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;steveklabnik.github.io&#x2F;jujutsu-tutorial&#x2F;sharing-code&#x2F;remotes.html&quot;&gt;add a remote&lt;&#x2F;a&gt; and &lt;code&gt;jj git push&lt;&#x2F;code&gt;. In brief detail, we can add a git remote:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;jj&lt;&#x2F;span&gt;&lt;span&gt; git remote add origin git@github.com:slightknack&#x2F;scrabble.git
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then we can set a bookmark named &lt;code&gt;master&lt;&#x2F;code&gt; pointing at the most recent commit:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;jj&lt;&#x2F;span&gt;&lt;span&gt; bookmark set master
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which we can then push to GitHub, the bookmark becoming the &lt;code&gt;master&lt;&#x2F;code&gt; branch:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;jj&lt;&#x2F;span&gt;&lt;span&gt; git push&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt; --allow-new
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The integration jj has with git is very cool! From the &lt;em&gt;little time&lt;&#x2F;em&gt; I’ve spent using jj and the &lt;em&gt;lot of time&lt;&#x2F;em&gt; I’ve spent reading about jj, I think that jj’s UI is much nice than git’s. On Lobsters, I observed that perhaps “jj is positioned to ameliorate the git world as TS ameliorated JS”. I’d like to live in that world; I’d better blog about jj more.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The approach I took to writing this project was essentially the approach that Casey Muratori outlines in his post &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;caseymuratori.com&#x2F;blog_0015&quot;&gt;&lt;em&gt;Semantic Compression&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;. I’m not going to explain it here, he does a much better job than I have space to. The core idea of this process is to add the next most obvious feature in the simplest way possible, not trying to abstract beforehand. Once a feature is working, gradually refactor out common ‘stack frames’ into structs, and functions that use those structs. Over time the codebase sort of organizes itself. I think this approach works really well when it comes to making game-like things, which makes sense: Muratori is a game programmer, after all.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;N.B. I kinda missed the whole AI train (long story) so all this code was written by hand, reading the documentation (e.g. the entire Zig language &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ziglang.org&#x2F;documentation&#x2F;master&quot;&gt;is just one page&lt;&#x2F;a&gt;!), etc. Mistakes are my own!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;a-note-on-comptime&quot;&gt;A note on comptime&lt;&#x2F;h2&gt;
&lt;p&gt;I’d like to walk through the file and pull out interesting bits of code, just to give you a feel for the project, and maybe introduce some bits of Zig I found cool. The whole project largely exists in a single ~500 line &lt;code&gt;main.zig&lt;&#x2F;code&gt; file. At the top of the file, I have two imports:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; std = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;@import&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;std&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; rl = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;@import&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;raylib&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Two pretty cool items of language design, right away:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;A top-level &lt;code&gt;const&lt;&#x2F;code&gt; like this means that this code is evaluated at compile-time (comptime)!&lt;&#x2F;li&gt;
&lt;li&gt;Symbols starting with &lt;code&gt;@&lt;&#x2F;code&gt;, like &lt;code&gt;@import&lt;&#x2F;code&gt;, are special to the compiler. &lt;code&gt;@import(&quot;std&quot;)&lt;&#x2F;code&gt; essentially adds a source file to the build, producing a struct, which we then can assign to a symbol, like &lt;code&gt;std&lt;&#x2F;code&gt;. Neat!&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;We see this idea of comptime echoed a lot. Modules are just comptime structs in other files. Types are first-class values at comptime. Generics are functions that return types at comptime. And so on.&lt;&#x2F;p&gt;
&lt;p&gt;After our imports, we embed some static resources in the binary, sounds and textures:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; image_table = @embedFile(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;.&#x2F;assets&#x2F;table-light.jpg&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; sound_place = @embedFile(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;.&#x2F;assets&#x2F;place.wav&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; sound_pickup = @embedFile(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;.&#x2F;assets&#x2F;pickup.wav&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; sound_tap = @embedFile(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;.&#x2F;assets&#x2F;tap.wav&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; sound_shuffle = @embedFile(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;.&#x2F;assets&#x2F;shuffle.wav&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The comptime function &lt;code&gt;@embedFile&lt;&#x2F;code&gt; is pretty cool, similar to the &lt;code&gt;include_bytes!&lt;&#x2F;code&gt; macro in Rust.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;structs-and-the-shape-of-a-stack-frame&quot;&gt;Structs and the shape of a stack frame&lt;&#x2F;h2&gt;
&lt;p&gt;As I programmed, I ended up organizing game state into a few different structs, one generic:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Grid(rows, cols)&lt;&#x2F;code&gt;: fixed-size grid of squares, each square may contain a tile.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Tile&lt;&#x2F;code&gt;: A tile with a single letter on it.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Rack&lt;&#x2F;code&gt;: Contains a &lt;code&gt;Grid(1, 7)&lt;&#x2F;code&gt; and a &lt;code&gt;Button&lt;&#x2F;code&gt;, which can be used to refill the rack.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Bag&lt;&#x2F;code&gt;: Shuffles all 98 scrabble tiles and returns them one by one, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;harddrop.com&#x2F;wiki&#x2F;Random_Generator&quot;&gt;similar to tetris&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Button&lt;&#x2F;code&gt;: A single button that can be clicked.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The &lt;code&gt;Grid&lt;&#x2F;code&gt; struct is generic. In Zig, this means it is a function that we call (at comptime!) with the number of rows and columns, to produce a concrete type with a statically-known size. We do this so we know how big of an array to allocate to hold all the tiles in the grid. I’m actually rather proud of this fact: by virtue of never using an allocator, this code never allocates on the heap! (Caveat, raylib internals.) Here’s how &lt;code&gt;Grid&lt;&#x2F;code&gt; is defined:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;Grid&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;comptime &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;num_rows&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;comptime &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;num_cols&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;return struct &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; Self = @This();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;rows&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;usize &lt;&#x2F;span&gt;&lt;span&gt;= num_rows,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;cols&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;usize &lt;&#x2F;span&gt;&lt;span&gt;= num_cols,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;posX&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;posY&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;tile_width&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;tile_height&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;gap&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; like some text in a book, left to right, top to bottom
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;tiles&lt;&#x2F;span&gt;&lt;span&gt;: [num_rows * num_cols]?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Tile&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; methods, etc. ...
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Raylib is essentially an immediate mode library for graphics. Meaning, each frame, we have to generate a sequence of draw events that will produce the picture we see on the screen. Each of the above structs (except for &lt;code&gt;Bag&lt;&#x2F;code&gt;) has an &lt;code&gt;update&lt;&#x2F;code&gt; method and a &lt;code&gt;draw&lt;&#x2F;code&gt; method that can be called each frame. It’s refreshingly simple.&lt;&#x2F;p&gt;
&lt;p&gt;Again, I wrote the code in a procedural style and ‘pulled out stack frames’ as I went along. I wasn’t trying to take an object-oriented approach, or confine structs to a given interface. These were the patterns that emerged in the code that I pulled out of &lt;code&gt;main&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I don’t know why, but I find this to be such a fun way to code. Here’s the method that draws the grid, for example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F;&#x2F; draw the grid background and all the tiles on the grid
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;draw&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Self&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;color&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;rl.Color&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; draw the grid background
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(0..self.rows) |row| {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(0..self.cols) |col| {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;r&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32 &lt;&#x2F;span&gt;&lt;span&gt;= @intCast(row);
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32 &lt;&#x2F;span&gt;&lt;span&gt;= @intCast(col);
&lt;&#x2F;span&gt;&lt;span&gt;            rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;drawRectangle&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;                self.posX + c * self.tile_width,
&lt;&#x2F;span&gt;&lt;span&gt;                self.posY + r * self.tile_height,
&lt;&#x2F;span&gt;&lt;span&gt;                self.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;width&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                self.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;height&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;                color,
&lt;&#x2F;span&gt;&lt;span&gt;            );
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; draw the tiles on top
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(self.tiles) |maybe_tile| {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(maybe_tile) |tile| {
&lt;&#x2F;span&gt;&lt;span&gt;            tile.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;draw&lt;&#x2F;span&gt;&lt;span&gt;(rl.Color.white, rl.Color.light_gray, rl.Color.black);
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Isn’t that so … satisfying? I mean sure, it’s not a beautiful Haskell one-liner, yet it contains &lt;em&gt;exactly&lt;&#x2F;em&gt; everything that needs to happen, no less, and no more.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;N.B. I also really love Zig’s block syntax &lt;code&gt;|...| { ... }&lt;&#x2F;code&gt; for &lt;code&gt;for&lt;&#x2F;code&gt; and &lt;code&gt;if&lt;&#x2F;code&gt;. The way Zig does nulls is very cool and I’ll have to write about it some more sometime.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The way I did tiles is pretty fun. Here’s a &lt;code&gt;Tile&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Tile &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;pos&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;rl.Vector2&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;width&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;height&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;hover&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;f32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;thick&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;letter&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that &lt;code&gt;rl.Vector2&lt;&#x2F;code&gt; is a type defined by raylib (&lt;code&gt;rl&lt;&#x2F;code&gt;) and essentially amounts to two &lt;code&gt;f32&lt;&#x2F;code&gt;s. Elsewhere, &lt;code&gt;hover&lt;&#x2F;code&gt; is the height the tile is floating above the ground, and &lt;code&gt;letter&lt;&#x2F;code&gt; is a byte representing the ASCII code for the letter on the tile.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;animations-falling-into-place&quot;&gt;Animations falling into place&lt;&#x2F;h2&gt;
&lt;p&gt;What’s really great is how naturally the tile animations fall out of this. When we place a &lt;code&gt;Tile&lt;&#x2F;code&gt; in a &lt;code&gt;Grid&lt;&#x2F;code&gt;, the grid stores it in a linearized array, &lt;code&gt;Grid.tiles&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; like some text in a book, left to right, top to bottom
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;tiles&lt;&#x2F;span&gt;&lt;span&gt;: [num_rows * num_cols]?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Tile&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When we update the grid each frame, we animate each tile in &lt;code&gt;tiles&lt;&#x2F;code&gt; towards the resting position it should be in. Here’s what that looks like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F;&#x2F; animate placed tiles towards their resting grid positions. should be called once per frame.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;update&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;: *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Self&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(0..self.rows) |row| {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(0..self.cols) |col| {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;r&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32 &lt;&#x2F;span&gt;&lt;span&gt;= @intCast(col);
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;i32 &lt;&#x2F;span&gt;&lt;span&gt;= @intCast(row);
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; guaranteed to be within bounds
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; index = self.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;toIndex&lt;&#x2F;span&gt;&lt;span&gt;(r, c).?;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; target = self.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;toTarget&lt;&#x2F;span&gt;&lt;span&gt;(r, c);
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; tile = &amp;amp;(self.tiles[index] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;orelse continue&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;            tile.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;settleInPlace&lt;&#x2F;span&gt;&lt;span&gt;(target);
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I have no clue whether there’s a better way to get a reference to &lt;code&gt;tile&lt;&#x2F;code&gt; than the approach I used. Surely there is, compared to this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; tile = &amp;amp;(self.tiles[index] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;orelse continue&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;tile.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;settleInPlace&lt;&#x2F;span&gt;&lt;span&gt;(target);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We have an array of optional tiles (&lt;code&gt;[]?Tile&lt;&#x2F;code&gt;) and a reference to an item in that array is a &lt;code&gt;*?Tile&lt;&#x2F;code&gt;, but we need a &lt;code&gt;*Tile&lt;&#x2F;code&gt;. I had fun here but there’s probably a very simple way to do this. I digress&lt;&#x2F;p&gt;
&lt;p&gt;We go through each tile and nudge it towards where it needs to be on the grid. The method &lt;code&gt;tile.settleInPlace&lt;&#x2F;code&gt; just nudges the tile towards the target position, and lowers the &lt;code&gt;hover&lt;&#x2F;code&gt;ing tile to the ground:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F;&#x2F; animate the tile towards a given target. should be called once per frame
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;settleInPlace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;: *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Tile&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;target&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;rl.Vector2&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    self.pos = rl.math.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;vector2Lerp&lt;&#x2F;span&gt;&lt;span&gt;(self.pos, target, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0.3&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    self.hover = rl.math.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;lerp&lt;&#x2F;span&gt;&lt;span&gt;(self.hover, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0.0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0.08&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here &lt;code&gt;lerp&lt;&#x2F;code&gt; is a &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;scratch.mit.edu&#x2F;projects&#x2F;40796964&#x2F;editor&#x2F;&quot;&gt;classic trick older than time&lt;&#x2F;a&gt;. I’m sure other people have their own names for this, but I don’t think it needs a name. I think of it as the &lt;code&gt;pos += (target - pos) &#x2F; speed&lt;&#x2F;code&gt; trick. In the APL tradition, if something is simple enough that it is about as long as its name, why name it?&lt;&#x2F;p&gt;
&lt;p&gt;We use a similar trick for when a tile is hovering over a grid. We want the tile to be “magnetically attracted” to the grid spaces but also follow the mouse. We can use the tension between two &lt;code&gt;lerp&lt;&#x2F;code&gt;s to make that happen:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F;&#x2F; animate the tile towards the mouse, biased towards the grid. should be called once per frame.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;followMouse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;: *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Tile&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;mouse&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;rl.Vector2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;snap&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;rl.Vector2&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; pos_mouse = rl.math.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;vector2Lerp&lt;&#x2F;span&gt;&lt;span&gt;(self.pos, mouse, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; pos_snap = rl.math.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;vector2Lerp&lt;&#x2F;span&gt;&lt;span&gt;(pos_mouse, snap, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0.2&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    self.pos = pos_snap;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The parameter &lt;code&gt;snap&lt;&#x2F;code&gt; is computed elsewhere, but it’s the screenspace coordinates of the nearest grid cell.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;N.B. I was considering keeping track of velocities to make the tile springy and give it some mass (another classic trick). Here’s what that looks like, if curious&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;vel += vel * friction + (target - pos) &#x2F; speed
&lt;&#x2F;span&gt;&lt;span&gt;pos += vel
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;What is really cool about this procedural stateless approach to animating tiles is that when we add a tile to a grid, or have it follow the mouse, it naturally smoothly travels to the right place. Complex dynamic behaviour is best driven by simple behavior compounded over time.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;randomness-and-the-bag&quot;&gt;Randomness and the bag&lt;&#x2F;h2&gt;
&lt;p&gt;One thing I wanted to get right was the &lt;code&gt;Bag&lt;&#x2F;code&gt;. I didn’t want to allocate anything, but I wanted the distribution of scrabble tiles to be correct. Well, the second part is easy, we just need a bag with each tile:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F;&#x2F; don&amp;#39;t ask
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;scrabble_bag&lt;&#x2F;span&gt;&lt;span&gt;: *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;98&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;u8 &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;EEEEEEEEEEEEAAAAAAAAAIIIIIIIIIOOOOOOOONNNNNNRRRRRRTTTTTTLLLLSSSSUUUUDDDDGGGBBCCMMPPFFHHVVWWYYKXJQZ&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Scrabble_letter_distributions&quot;&gt;Thank you Wikipedia&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We’ll just allocate, on the stack I suppose, a single large struct with space to hold all these letters:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Bag &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;scrambled&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;98&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;N.B. I don’t know how Zig internally deals with large structs like this. I know that, in principle, when a function is called, structs are passed by value, “making a fresh immutable copy”. I would hope that in practice Zig optimizes this to a reference to an earlier stack frame or similar.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;All we’ll do is shuffle our &lt;code&gt;scrabble_bag&lt;&#x2F;code&gt; into &lt;code&gt;Bag.scrambled&lt;&#x2F;code&gt;, then empty out the bag by incrementing &lt;code&gt;next&lt;&#x2F;code&gt;, shuffling again when we reach the end. Oh. How does one shuffle in Zig? I will note that this was a little non-trivial to find docs for because there is a deprecated API that shows up higher in the search results, but the long story short is we want to use &lt;code&gt;std.Random&lt;&#x2F;code&gt; via &lt;code&gt;std.crypto.random&lt;&#x2F;code&gt;, and that’s something you can look up.&lt;&#x2F;p&gt;
&lt;p&gt;Here’s the code that shuffles the bag:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;fresh&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Bag &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; rand = std.crypto.random;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;loc&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;98&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;u8 &lt;&#x2F;span&gt;&lt;span&gt;= scrabble_bag.*;
&lt;&#x2F;span&gt;&lt;span&gt;    rand.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;shuffle&lt;&#x2F;span&gt;&lt;span&gt;(u8, &amp;amp;loc);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;return Bag&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        .scrambled = loc,
&lt;&#x2F;span&gt;&lt;span&gt;        .next = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Figuring out &lt;code&gt;var loc: [98]u8&lt;&#x2F;code&gt; also took a little work. Zig doesn’t have full Hindley-Milner type inference, as Rust does. Sometimes you have to guide the compiler along by using &lt;code&gt;@as&lt;&#x2F;code&gt; or explicit bindings. Not necessarily a bad thing, it’s good to know what types are flowing through the program. A good balance between Rust’s type inference magic and Austral’s &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;borretti.me&#x2F;article&#x2F;introducing-austral#anti-features&quot;&gt;purposeful lack thereof&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;N.B. This makes total sense, in the context of Zig’s comptime! When generic types are built function calls, type information flows in one direction. Flowing program information backwards is what we see in languages like Prolog, where rules can be thought of as bidirectional functions. I briefly explored this direction in a compiler I am working on, which has (had?) first-class support for datalog-like queries. “Type inference as a comptime datalog query.” Maybe someday.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Okay, and just for completion’s sake, here’s the rest of &lt;code&gt;Bag&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F;&#x2F; pick a tile from the bag. if the bag is empty, replace with a fresh bag.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;pick&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;: *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Bag&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;u8 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; drawn = self.scrambled[self.next];
&lt;&#x2F;span&gt;&lt;span&gt;    self.next += &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(self.next &amp;gt;= self.scrambled.len) {
&lt;&#x2F;span&gt;&lt;span&gt;        self.* = Bag.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;fresh&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt; drawn;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I was surprised that &lt;code&gt;std.crypto.random&lt;&#x2F;code&gt; worked out of the box for the wasm build of the demo. From my experience with Rust and &lt;code&gt;rand&lt;&#x2F;code&gt;, this is not always something that automatically works.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-main-man&quot;&gt;My main man&lt;&#x2F;h2&gt;
&lt;p&gt;We’ve already talked about most of the project, structured as follows:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Imports and embeddings&lt;&#x2F;li&gt;
&lt;li&gt;Structs and methods&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;main&lt;&#x2F;code&gt; function
&lt;ul&gt;
&lt;li&gt;Startup code&lt;&#x2F;li&gt;
&lt;li&gt;Per-frame loop&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I’d like to talk about the &lt;code&gt;main&lt;&#x2F;code&gt; function, because it’s the beating heart of this whole thing. As I mentioned, I wrote this project by writing a main function, and then pulling out functions and bundles of local variables as things got repetitive. So &lt;code&gt;main&lt;&#x2F;code&gt; really is the driver of the whole codebase, both literally and conceptually.&lt;&#x2F;p&gt;
&lt;p&gt;Raylib is delightful to work with. Here’s how we set up our window:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;anyerror&lt;&#x2F;span&gt;&lt;span&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;void &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; screenWidth = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;800&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; screenHeight = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;600&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;initWindow&lt;&#x2F;span&gt;&lt;span&gt;(screenWidth, screenHeight, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;game game&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;initAudioDevice&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;setTargetFPS&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;defer&lt;&#x2F;span&gt;&lt;span&gt; rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;closeWindow&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;defer&lt;&#x2F;span&gt;&lt;span&gt; rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;closeAudioDevice&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; Startup code and per-frame loop
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I love how I can use &lt;code&gt;defer&lt;&#x2F;code&gt; here. It’s a nice way to pair together functions that must be called together, but at different times. A lot of old C APIs expect this sort of “manual nesting by the programmer” to enter and exit over e.g. taking a callback. I prefer &lt;code&gt;defer&lt;&#x2F;code&gt; over RAII, though. At least in this context: raylib is simple and global and single-threaded, what do I know.&lt;&#x2F;p&gt;
&lt;p&gt;The startup code that comes after builds a lot of structs. It looks like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; grid = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;GridBoard&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    .posX = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;175&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    .posY = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;45&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    .tile_width = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    .tile_height = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    .gap = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    .tiles = [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;]?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Tile&lt;&#x2F;span&gt;&lt;span&gt;{null} ** (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;15 &lt;&#x2F;span&gt;&lt;span&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; more structs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I considered factoring this out into a bunch of unique &lt;code&gt;init&lt;&#x2F;code&gt; functions, but that’s like squeezing a water balloon. The lines are just going to pop up somewhere else in the codebase, and I’d rather have all this initialization code in the same place for easy tweaking. Maybe if the project were bigger.&lt;&#x2F;p&gt;
&lt;p&gt;Then we load all the sound and image data for the game. Remember, this was embedded into the binary earlier with &lt;code&gt;@embedFile&lt;&#x2F;code&gt;. Honestly I am so impressed by how nice and logically organized raylib’s API is, what a treat:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; image_table_mem = rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;loadImageFromMemory&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;.jpg&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, image_table);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; image_table_tex = rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;loadTextureFromImage&lt;&#x2F;span&gt;&lt;span&gt;(image_table_mem);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; sound_pickup_mem = rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;loadWaveFromMemory&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;.wav&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, sound_pickup);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; sound_place_mem = rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;loadWaveFromMemory&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;.wav&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, sound_place);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; sound_tap_mem = rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;loadWaveFromMemory&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;.wav&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, sound_tap);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; sound_shuffle_mem = rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;loadWaveFromMemory&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;.wav&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, sound_shuffle);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; sound_pickup_wav = rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;loadSoundFromWave&lt;&#x2F;span&gt;&lt;span&gt;(sound_pickup_mem);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; sound_place_wav = rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;loadSoundFromWave&lt;&#x2F;span&gt;&lt;span&gt;(sound_place_mem);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; sound_tap_wav = rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;loadSoundFromWave&lt;&#x2F;span&gt;&lt;span&gt;(sound_tap_mem);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; sound_shuffle_wav = rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;loadSoundFromWave&lt;&#x2F;span&gt;&lt;span&gt;(sound_shuffle_mem);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; per-frame loop.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If only I were better at naming things, haha.&lt;&#x2F;p&gt;
&lt;p&gt;Onto the per-frame loop. We start by clearing and drawing the background. Another useful appearance of &lt;code&gt;defer&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span&gt;(!rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;windowShouldClose&lt;&#x2F;span&gt;&lt;span&gt;()) {
&lt;&#x2F;span&gt;&lt;span&gt;    rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;beginDrawing&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;defer&lt;&#x2F;span&gt;&lt;span&gt; rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;endDrawing&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;clearBackground&lt;&#x2F;span&gt;&lt;span&gt;(rl.Color.white);
&lt;&#x2F;span&gt;&lt;span&gt;    rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;drawTexture&lt;&#x2F;span&gt;&lt;span&gt;(image_table_tex, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, rl.Color.white);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;defer&lt;&#x2F;span&gt;&lt;span&gt; rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;drawFPS&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ... update and draw
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I wanted to draw the FPS counter on top of everything, which I can do by drawing it last through &lt;code&gt;defer&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There’s a lot of pretty dense game state updating that I don’t want to bore you with, but it’s nothing complicated. I probably should break it up into a few functions. Here’s how we update and draw the grid:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span&gt;grid.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;update&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;grid.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;draw&lt;&#x2F;span&gt;&lt;span&gt;(rl.Color.dark_brown.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;alpha&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0.2&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And to give you an idea of the logic, here’s the logic for trying to pick up a tile:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;zig&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-zig &quot;&gt;&lt;code class=&quot;language-zig&quot; data-lang=&quot;zig&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(mouse_click) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(grid.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;pickUp&lt;&#x2F;span&gt;&lt;span&gt;(tile.pos)) |got_tile| {
&lt;&#x2F;span&gt;&lt;span&gt;        tile = got_tile;
&lt;&#x2F;span&gt;&lt;span&gt;        tile_visible = true;
&lt;&#x2F;span&gt;&lt;span&gt;        rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;playSound&lt;&#x2F;span&gt;&lt;span&gt;(sound_place_wav);
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;else if &lt;&#x2F;span&gt;&lt;span&gt;(rack.grid.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;pickUp&lt;&#x2F;span&gt;&lt;span&gt;(tile.pos)) |got_tile| {
&lt;&#x2F;span&gt;&lt;span&gt;        tile = got_tile;
&lt;&#x2F;span&gt;&lt;span&gt;        tile_visible = true;
&lt;&#x2F;span&gt;&lt;span&gt;        rl.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;playSound&lt;&#x2F;span&gt;&lt;span&gt;(sound_pickup_wav);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There’s similar code for placing a tile, how to swap a tile, updating the tile to follow the mouse, and so on.&lt;&#x2F;p&gt;
&lt;p&gt;Again, look at that beautiful raylib API for playing a sound!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;&#x2F;h1&gt;
&lt;p&gt;I had a lot of fun messing around with Zig and raylib!&lt;&#x2F;p&gt;
&lt;p&gt;I really do enjoy picking up new tools like this. Trying out a project like this is low stakes, and experience is the best teacher. I have read a lot of Zig code, but this is the first time I really write something. Thank you Andrew Kelley and everyone who works on Zig and, well, @raysan5 for raylib (and @Not-Nik for the bindings)!&lt;&#x2F;p&gt;
&lt;p&gt;One thing I missed coming from Rust was pattern matching. Zig doesn’t have pattern matching, I suppose? Reading some discussion online, it seems to be that Zig’s &lt;code&gt;switch&lt;&#x2F;code&gt; statement—the moral equivalent to Rust’s &lt;code&gt;match&lt;&#x2F;code&gt;—compiles to a jump table, but pattern matching can lead to non-obvious control flow. I can see how that goes against the ethos of Zig, but man pattern matching would be nice. Maybe there’s a &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;TheHonestHare&#x2F;zkinder&quot;&gt;library that emulates pattern matching&lt;&#x2F;a&gt;? Could a library generate pattern matching code at comptime? Who knows!&lt;&#x2F;p&gt;
&lt;p&gt;I don’t know if I will add more to this game, but if I do, stay tuned. I have been thinking about what it would take to add online multiplayer. I have also been sketching out a fun little CRDT library. We’ll see what happens.&lt;&#x2F;p&gt;
&lt;p&gt;Oh yeah! and I totally forgot to touch on &lt;code&gt;build.zig&lt;&#x2F;code&gt;! I’ll have to touch on that the next time I write about Zig.&lt;&#x2F;p&gt;
&lt;p&gt;And jujutsu was really fun. I even got to use &lt;code&gt;amend&lt;&#x2F;code&gt;. Another topic for another post!&lt;&#x2F;p&gt;
&lt;p&gt;Merry Christmas, and to all a good night!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Intelligence and the importance of consistency</title>
        <published>2024-08-13T00:00:00+00:00</published>
        <updated>2024-08-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/constraints/"/>
        <id>https://slightknack.dev/blog/constraints/</id>
        
        <summary type="html">&lt;blockquote&gt;
&lt;p&gt;Note: This post was published before the release of o1 on 2024-9-12.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;People are quick to point out that Large Language Models (LLMs) tend to
hallucinate facts and lack the ability to reason. LLMs are not grounded
in reality. Hallucination is an architectural limitation due to how
Transformers, as auto-regressive sequence predictors, are constructed.&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Dealing with Cyclic Data in Rust, Part I</title>
        <published>2022-07-22T00:00:00+00:00</published>
        <updated>2022-07-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/rust-cycles/"/>
        <id>https://slightknack.dev/blog/rust-cycles/</id>
        
        <summary type="html">&lt;blockquote&gt;
&lt;p&gt;Or, a &lt;em&gt;GhostCell Deep Dive&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In this two-part series, we build &lt;em&gt;GhostCell&lt;&#x2F;em&gt; from first principles. In &lt;em&gt;Part I&lt;&#x2F;em&gt; we go over the underlying theory required to understand GhostCell. This post’s been sitting on my hard drive for about 9 months, so I’ve decided to bite the bullet, split what I’ve written in two, and hit publish on &lt;em&gt;Part I&lt;&#x2F;em&gt;. Hope you enjoy!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Over the past month or so, something I’ve repeatedly run into is &lt;em&gt;GhostCell&lt;&#x2F;em&gt;, a technique that (ab)uses Rust’s lifetime system to detach ownership of data from the permission to mutate it. In short, this makes it possible to write datatypes that rely on shared interior mutability (think doubly-linked lists and other cyclical graph-like structures). In this post I wanted to explore Rust’s lifetime system to explain GhostCell from first principles, and why it’s kinda a big deal.&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Building a Rust Mentality</title>
        <published>2022-07-21T00:00:00+00:00</published>
        <updated>2022-07-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/shift/"/>
        <id>https://slightknack.dev/blog/shift/</id>
        
        <summary type="html">&lt;p&gt;What really helped me begin to &lt;em&gt;grok&lt;&#x2F;em&gt; how Rust really worked was working towards building an understanding of how its &lt;em&gt;compiler&lt;&#x2F;em&gt; works.&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Late Night Conversations about Nothing in Particular</title>
        <published>2022-07-15T00:00:00+00:00</published>
        <updated>2022-07-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/late/"/>
        <id>https://slightknack.dev/blog/late/</id>
        
        <summary type="html">&lt;h2 id=&quot;the-beginning-of-the-end-of-today&quot;&gt;The Beginning of the End (of Today)&lt;&#x2F;h2&gt;
&lt;p&gt;As I write this today, it’s tomorrow.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;and-we-re-talking-about-school&quot;&gt;And… We’re talking about school?&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;N.B. This post is not about school, bear with me.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Throughout the school year, I’m usually pretty consistent with my sleep schedule. It’s like a rhythm, every hour of my day planned out. I wake up at 5:55 exactly, do some morning studying (it’s always so much easier to get stuff done in the morning), shower an hour later, breakfast, and I’m out the door.&lt;&#x2F;p&gt;
&lt;p&gt;School’s rhythmic too. This was my last year of high school, my senior year. I think I overdid it a bit, to be honest. 6 APs and 2 college math courses, on top of, you know, everything else that goes on in life, is a bit much for anyone, &lt;em&gt;especially&lt;&#x2F;em&gt; for a simple-minded guy like me. I appreciate the rhythm school brings to my life.&lt;&#x2F;p&gt;
&lt;p&gt;So why am I writing this at 1 AM?&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Traits as implicit conversion</title>
        <published>2022-02-28T00:00:00+00:00</published>
        <updated>2022-02-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/passerine/traits-as/"/>
        <id>https://slightknack.dev/passerine/traits-as/</id>
        
        <content type="html" xml:base="https://slightknack.dev/passerine/traits-as/">&lt;p&gt;The joy of writing a &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;passerine.io&quot;&gt;new programming language&lt;&#x2F;a&gt; is coming up with novel ideas and seeing if they stick.&lt;&#x2F;p&gt;
&lt;p&gt;The challenge I’m attempting to solve stems from dealing with different types of objects that share common structure or behavior. For this reason I’ve been thinking a lot about how to rectify open&#x2F;closed enumerations, traits, and type constructors.&lt;&#x2F;p&gt;
&lt;p&gt;Traditional object-oriented languages deal with this through the use of inheritance. For example since both a &lt;code&gt;Wizard&lt;&#x2F;code&gt; and a &lt;code&gt;Person&lt;&#x2F;code&gt; have a &lt;code&gt;name&lt;&#x2F;code&gt;, they may both inherit from a single &lt;code&gt;Named&lt;&#x2F;code&gt; class. In Java, we may write this as:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Named &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;name;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Person &lt;&#x2F;span&gt;&lt;span&gt;extends &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Named &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;int &lt;&#x2F;span&gt;&lt;span&gt;age;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Wizard &lt;&#x2F;span&gt;&lt;span&gt;extends &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Named &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;title;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;int &lt;&#x2F;span&gt;&lt;span&gt;skill;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is all well and dandy, but problems quickly arise. Java only supports single inheritance, so if we want a new class to extend both &lt;code&gt;Named&lt;&#x2F;code&gt; and, say &lt;code&gt;Aged&lt;&#x2F;code&gt;, we’d either have to create a new class (like &lt;code&gt;NamedAndAged&lt;&#x2F;code&gt;, gross), or use a language with &lt;em&gt;multiple inheritance&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Multiple inheritance sucks for other reasons, though, mostly due to the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Multiple_inheritance#The_diamond_problem&quot;&gt;diamond dependency problem&lt;&#x2F;a&gt;. If we even create a class that inherits from two superclasses with the same field, which field gets used? Does the object have two fields?&lt;&#x2F;p&gt;
&lt;p&gt;This is a problem as old as the hills, and it’s why we’ve developed sayings—like ‘always choose composition over inheritance’—that have been passed down from developer to developer, generation after generation.&lt;&#x2F;p&gt;
&lt;p&gt;We don’t have to be stuck with the pains inheritance, though! Inheritance is really just ensuring that different objects share certain structure and&#x2F;or behavior. Ultimately this is what composition over inheritance means: Instead of having a &lt;code&gt;Person&lt;&#x2F;code&gt; that is &lt;code&gt;Named&lt;&#x2F;code&gt;, just make a &lt;code&gt;Person&lt;&#x2F;code&gt; have a &lt;code&gt;Name&lt;&#x2F;code&gt;, and pass that &lt;code&gt;Name&lt;&#x2F;code&gt; around when required:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Name &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;name;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Person &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;int &lt;&#x2F;span&gt;&lt;span&gt;age;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Name &lt;&#x2F;span&gt;&lt;span&gt;name;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;While nice, this required that person carry around a &lt;code&gt;name&lt;&#x2F;code&gt; field; if the name can be derived from existing class data, this may be redundant. We could use a method, of course:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;java&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-java &quot;&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Person &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;() { 
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ... 
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But the problem here is that there’s no real &lt;em&gt;consistency&lt;&#x2F;em&gt; between the various ways of representing that a &lt;code&gt;Person&lt;&#x2F;code&gt; has a name. Do we access a field, call a method, etc?&lt;&#x2F;p&gt;
&lt;p&gt;But you know the solution to this! Just use typeclasses&#x2F;traits&#x2F;interfaces, you shout! Instead of adding methods and fields ad-hoc, we declare a shared &lt;code&gt;trait&lt;&#x2F;code&gt; (to use the Rust parlance) with common behavior:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;Rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-Rust &quot;&gt;&lt;code class=&quot;language-Rust&quot; data-lang=&quot;Rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;pub trait &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Named &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then, if we have a person, we can implement &lt;code&gt;Named&lt;&#x2F;code&gt; for &lt;code&gt;Person&lt;&#x2F;code&gt; to show that a person indeed has a name:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;Rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-Rust &quot;&gt;&lt;code class=&quot;language-Rust&quot; data-lang=&quot;Rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Person &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;age&lt;&#x2F;span&gt;&lt;span&gt;:  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Named for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Person &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    name(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.name.to_string()
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can access this field using regular method call syntax, like &lt;code&gt;person.name()&lt;&#x2F;code&gt;. If we implement another trait that &lt;em&gt;also&lt;&#x2F;em&gt; has a name method, then we must use Rust’s Uniform Function Call Syntax (UFCS) to disambiguate: &lt;code&gt;Named::name(&amp;amp;person)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Whatever you call it, the core idea behind traits&#x2F;typeclasses&#x2F;interfaces&#x2F;etc. is simple: define a single interface with a number of behaviors through which the underlying object is accessed.&lt;&#x2F;p&gt;
&lt;p&gt;My largest issue with these systems is that &lt;em&gt;another layer&lt;&#x2F;em&gt; on top of the language itself. This description may not be entirely clear, so let’s jump into some examples in Passerine:&lt;&#x2F;p&gt;
&lt;h1 id=&quot;a-modest-proposal&quot;&gt;A modest proposal&lt;&#x2F;h1&gt;
&lt;p&gt;Say we have a struct; it’s for a &lt;code&gt;Person&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;type Person = {
&lt;&#x2F;span&gt;&lt;span&gt;    name: String,
&lt;&#x2F;span&gt;&lt;span&gt;    age:  Nat,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If we wanted to make a new &lt;code&gt;Person&lt;&#x2F;code&gt;, we’d just construct it:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Person {
&lt;&#x2F;span&gt;&lt;span&gt;    name: &amp;quot;Gerald&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    age:  69,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can write functions that update &lt;code&gt;Person&lt;&#x2F;code&gt;, too:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;birthday = Person { name, age          } 
&lt;&#x2F;span&gt;&lt;span&gt;        -&amp;gt; Person { name, age: age + 1 }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So far, this is all pretty standard. When you think about it, constructing a &lt;code&gt;Person&lt;&#x2F;code&gt; is just wrapping a bare struct in the &lt;code&gt;Person&lt;&#x2F;code&gt; newtype. In fact, this is completely valid:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;inner = { name: &amp;quot;Bob&amp;quot;, age: 27 }
&lt;&#x2F;span&gt;&lt;span&gt;bob = Person inner
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In essence, &lt;code&gt;Person&lt;&#x2F;code&gt; is a constructor: a function that takes some data and produces some data of that type:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Person : { name: String, age: Nat } -&amp;gt; Person
&lt;&#x2F;span&gt;&lt;span&gt;       = { name,         age      } -&amp;gt; Person { name, age }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In fact, for any type we define, we essentially get the following constructor for free:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Type : Inner -&amp;gt; Type
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By default, &lt;code&gt;Inner&lt;&#x2F;code&gt; is a single type: it’s literally the inner contents of &lt;code&gt;Type&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;dynamic-dispatch&quot;&gt;Dynamic dispatch&lt;&#x2F;h1&gt;
&lt;p&gt;This brings me to traits. A trait is essentially a set of different objects that share the same behavior. In Rust, for example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;trait &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Animal &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;feed&lt;&#x2F;span&gt;&lt;span&gt;()  -&amp;gt; String;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; String;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Anything that you can &lt;code&gt;feed&lt;&#x2F;code&gt; or &lt;code&gt;say&lt;&#x2F;code&gt; can be defined to be an &lt;code&gt;Animal&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Cat
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; A cat is an Animal
&lt;&#x2F;span&gt;&lt;span&gt;impl Animal for Cat {
&lt;&#x2F;span&gt;&lt;span&gt;    fn feed()  { &amp;quot;not hungry&amp;quot;.to_string() }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;speak&lt;&#x2F;span&gt;&lt;span&gt;() {      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;meow!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.to_string() }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Traits are useful for modeling systems that expect different objects with defined shared behavior. For example, we can define a trait that represents &lt;code&gt;Iterator&lt;&#x2F;code&gt; over an arbitrary stream:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; Abridged from Rust&amp;#39;s standard library
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;trait &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Iterator &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Item&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;mut &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; Option&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;Self::&lt;&#x2F;span&gt;&lt;span&gt;Item&amp;gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Anything has a notion of being advanced via &lt;code&gt;next&lt;&#x2F;code&gt;, whether that be moving a cursor through an array or traversing nodes in a tree, can be used as an &lt;code&gt;Iterator&lt;&#x2F;code&gt;. For example, here’s how we might iterate through a &lt;code&gt;Vec&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;VecIter&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    index: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    vec:   Vec&amp;lt;T&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; Ignoring some lifetime stuff for the sake of simplicity
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T&amp;gt; Iterator for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;VecIter&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Item &lt;&#x2F;span&gt;&lt;span&gt;= T;
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;mut &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; Option&amp;lt;T&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.index += &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.vec.get(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.index - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;vec_to_iter&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;vec&lt;&#x2F;span&gt;&lt;span&gt;: Vec&amp;lt;T&amp;gt;) -&amp;gt; VecIter {
&lt;&#x2F;span&gt;&lt;span&gt;    VecIter { index: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, vec }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That’s simple enough. calling &lt;code&gt;vec_to_iter(vec![1, 2, 3])&lt;&#x2F;code&gt; will produce a &lt;code&gt;VecIter&lt;&#x2F;code&gt; which can be used as an &lt;code&gt;impl Iterator&amp;lt;Item=usize&amp;gt;&lt;&#x2F;code&gt;, an iterator over the numbers 1, 2, and 3.&lt;&#x2F;p&gt;
&lt;p&gt;But if you were to represent an iterator as an &lt;em&gt;actual type&lt;&#x2F;em&gt;, how would you go about doing that?&lt;&#x2F;p&gt;
&lt;p&gt;Well, we know that an iterator produces &lt;code&gt;Item&lt;&#x2F;code&gt;s of a certain type, and has a single function that advances state:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Iterator&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;Item&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;: Box&amp;lt;dyn FnMut() -&amp;gt; Option&amp;lt;Item&amp;gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then why don’t we just have &lt;code&gt;vec_to_iter&lt;&#x2F;code&gt; return, well, an &lt;code&gt;Iterator&lt;&#x2F;code&gt;?&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; Again, completely ignoring the borrow checker lol
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;vec_to_iter&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;vec&lt;&#x2F;span&gt;&lt;span&gt;: Vec&amp;lt;T&amp;gt;) -&amp;gt; Iterator {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; index = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; vec = vec;
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    Iterator {
&lt;&#x2F;span&gt;&lt;span&gt;        next: Box::new(|| {
&lt;&#x2F;span&gt;&lt;span&gt;            index += &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;            vec.get(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.index - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        })
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see, an iterator is just a concrete type containing a higher-order function. In this non-trait version, implementing &lt;code&gt;Iterator&lt;&#x2F;code&gt; is as simple as &lt;em&gt;constructing&lt;&#x2F;em&gt; Iterator.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; iter = vec_to_iter(vec![&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;(vec.next)(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; Some(1)
&lt;&#x2F;span&gt;&lt;span&gt;(vec.next)(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; Some(2)
&lt;&#x2F;span&gt;&lt;span&gt;(vec.next)(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; Some(3)
&lt;&#x2F;span&gt;&lt;span&gt;(vec.next)(); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; None
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Rust makes this a bit harder because none of the code we just wrote would actually compile, but this should illustrate the point.&lt;&#x2F;p&gt;
&lt;p&gt;But the point is: traits can be represented as plain old types.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Aside:&lt;&#x2F;strong&gt; Agda and inference&lt;&#x2F;p&gt;
&lt;p&gt;TODO: Write about how Agda builds off this, it’s really cool!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Anyway, let’s hop back to Passerine.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;wizards-are-people-too&quot;&gt;Wizards are people too.&lt;&#x2F;h1&gt;
&lt;p&gt;Starting with our definition for &lt;code&gt;Person&lt;&#x2F;code&gt; from earlier, let’s also define a &lt;code&gt;Wizard&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;type Person = {
&lt;&#x2F;span&gt;&lt;span&gt;    name: String,
&lt;&#x2F;span&gt;&lt;span&gt;    age:  Nat,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;type Wizard = {
&lt;&#x2F;span&gt;&lt;span&gt;    title: String,
&lt;&#x2F;span&gt;&lt;span&gt;    name: String,
&lt;&#x2F;span&gt;&lt;span&gt;    skill:  Nat,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now wizards are people too. All wizards are actually as physically fit as a 25 year old (how else do you think they are so darn fast?), but their outward age-defined appearance is a pure function of &lt;code&gt;skill&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So let’s say we have a function that takes a &lt;code&gt;Person&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;call_for_dinner = Person { name, .. } -&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    println &amp;quot;Hey {name}, it&amp;#39;s time for dinner! Come and eat!&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now wizards are people too: wouldn’t it be nice if we could call our wizard friends over for dinner as well?&lt;&#x2F;p&gt;
&lt;p&gt;We could write a conversion function that temporarily converts a &lt;code&gt;Wizard&lt;&#x2F;code&gt; into a &lt;code&gt;Person&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;wizard_to_person = Wizard { title, name, skill } 
&lt;&#x2F;span&gt;&lt;span&gt;    -&amp;gt; Person {
&lt;&#x2F;span&gt;&lt;span&gt;        name: &amp;quot;{name} the {title}&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        age:  25 + skill &#x2F; 10,
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So if we have a &lt;code&gt;Wizard&lt;&#x2F;code&gt;, say &lt;code&gt;merlin&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;merlin = Wizard {
&lt;&#x2F;span&gt;&lt;span&gt;    title: &amp;quot;Wise&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    name:  &amp;quot;Merlin&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    skill: 930,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can call &lt;code&gt;merlin&lt;&#x2F;code&gt; over to dinner:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;call_to_dinner (wizard_to_person merlin)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Hey Merlin the Wise, it’s time for dinner! Come and eat!&lt;&#x2F;p&gt;
&lt;p&gt;The only thing missing for this to be a trait system would be some way to convert &lt;code&gt;merlin&lt;&#x2F;code&gt; to a &lt;code&gt;Person&lt;&#x2F;code&gt; automatically…&lt;&#x2F;p&gt;
&lt;h1 id=&quot;from-a-to-b&quot;&gt;from A to B&lt;&#x2F;h1&gt;
&lt;p&gt;So, back to constructors.&lt;&#x2F;p&gt;
&lt;p&gt;Remember that when we’re constructing a &lt;code&gt;Person&lt;&#x2F;code&gt;, &lt;code&gt;Person&lt;&#x2F;code&gt; essentially serves as a function with the following type:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Person : { name: String, age: Nat } -&amp;gt; Person
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In other words, we take a record (i.e. struct) representing a person, and produce a semi-opaque object of type &lt;code&gt;Person&lt;&#x2F;code&gt;. Reasonable enough.&lt;&#x2F;p&gt;
&lt;p&gt;But what if we could make &lt;code&gt;Person&lt;&#x2F;code&gt; construct over additional types?&lt;&#x2F;p&gt;
&lt;p&gt;The most obvious extension would be some sort of row polymorphism (as Passerine aims to eventually be row-polymorphic, in the tradition of ML-style languages). If we provide a record with additional fields to &lt;code&gt;Person&lt;&#x2F;code&gt;, person should ignore those fields:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Person { 
&lt;&#x2F;span&gt;&lt;span&gt;    name: &amp;quot;Joe&amp;quot;, 
&lt;&#x2F;span&gt;&lt;span&gt;    age: &amp;quot;5&amp;quot;, 
&lt;&#x2F;span&gt;&lt;span&gt;    birthday: &amp;quot;2022-05-23&amp;quot; 
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this case, the &lt;code&gt;birthday&lt;&#x2F;code&gt; field would be ignored. If we were to write this as a type, we could say that:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Person : { name: Nat, age: String } 
&lt;&#x2F;span&gt;&lt;span&gt;       | { name: Nat, age: String, .. } -&amp;gt; Person
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Remember that &lt;code&gt;|&lt;&#x2F;code&gt; is a sum type (i.e. enum). This is a bit redundant, as the former type is a subtype of the latter.&lt;&#x2F;p&gt;
&lt;p&gt;Taking some more creative liberties, let’s say that we want a person constructed with no &lt;code&gt;age&lt;&#x2F;code&gt; to take on a default value of &lt;code&gt;0&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;jack = Person { name: &amp;quot;Baby Jack&amp;quot; }
&lt;&#x2F;span&gt;&lt;span&gt;jack.age
&lt;&#x2F;span&gt;&lt;span&gt;-------- this is 0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Whether this is a good idea or not is debatable, but it wouldn’t be too hard to write as a function:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;baby_person = { name } -&amp;gt; Person { name, age: 0 }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If &lt;code&gt;Person&lt;&#x2F;code&gt; accepted this as well, we’d write the type of the &lt;code&gt;Person&lt;&#x2F;code&gt; constructor function as:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Person : { name: String, age: Nat }
&lt;&#x2F;span&gt;&lt;span&gt;       | { name: String, age: Nat, .. }
&lt;&#x2F;span&gt;&lt;span&gt;       | { name: String }
&lt;&#x2F;span&gt;&lt;span&gt;      -&amp;gt; Person
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let’s get a little crazy. Remember our &lt;code&gt;Wizards&lt;&#x2F;code&gt; from earlier? what if it was possible to construct a &lt;code&gt;Person&lt;&#x2F;code&gt; from a &lt;code&gt;Wizard&lt;&#x2F;code&gt;, you know, using our &lt;code&gt;wizard_to_person&lt;&#x2F;code&gt; routine:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;merlin = Wizard {
&lt;&#x2F;span&gt;&lt;span&gt;    title: &amp;quot;Wise&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    name:  &amp;quot;Merlin&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    skill: 930,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Person merlin
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This would mean that the &lt;code&gt;Person&lt;&#x2F;code&gt; constructor could really take anything of the following type:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Person : { name: String, age: Nat }
&lt;&#x2F;span&gt;&lt;span&gt;       | { name: String, age: Nat, .. }
&lt;&#x2F;span&gt;&lt;span&gt;       | { name: String }
&lt;&#x2F;span&gt;&lt;span&gt;       | Wizard
&lt;&#x2F;span&gt;&lt;span&gt;       | ...
&lt;&#x2F;span&gt;&lt;span&gt;      -&amp;gt; Person
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In all these cases — wizards, row polymorphism, default parameters, or otherwise, what we’re trying to do is simple — treat a some type that isn’t a &lt;code&gt;Person&lt;&#x2F;code&gt; as a &lt;code&gt;Person&lt;&#x2F;code&gt; by extending the constructor. By extending the constructor, we’re essentially treating &lt;code&gt;Person&lt;&#x2F;code&gt; in the similitude of a trait: a common target for shared behavior, namely having a &lt;code&gt;name&lt;&#x2F;code&gt; and an &lt;code&gt;age&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In essence, we want &lt;em&gt;open membership&lt;&#x2F;em&gt; over &lt;code&gt;Person&lt;&#x2F;code&gt;’s constructor. What if we could define our own &lt;code&gt;Person T&lt;&#x2F;code&gt;, creating people from arbitrary people-likes of type &lt;code&gt;T&lt;&#x2F;code&gt;?&lt;&#x2F;p&gt;
&lt;p&gt;Something I’ve been considering is a &lt;code&gt;impl ... from&lt;&#x2F;code&gt; syntax, specifically for this purpose:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;-- So that `Person Wizard` works
&lt;&#x2F;span&gt;&lt;span&gt;impl Person from Wizard = 
&lt;&#x2F;span&gt;&lt;span&gt;    Wizard { title, name, skill } 
&lt;&#x2F;span&gt;&lt;span&gt;-&amp;gt; Person {
&lt;&#x2F;span&gt;&lt;span&gt;    name: &amp;quot;{name} the {title}&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    age:  25 + skill &#x2F; 10,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This, essentially, would add a case to &lt;code&gt;Person&lt;&#x2F;code&gt;’s constructor so that constructing a &lt;code&gt;Person&lt;&#x2F;code&gt; from a wizard now makes sense. This essentially acts as a form of dynamic dispatch!&lt;&#x2F;p&gt;
&lt;p&gt;In fact, we’re not limited to the second type being a named type. We could implement baby-by-default (the case where the default age is zero) in the following manner:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;impl Person from { name: String } =
&lt;&#x2F;span&gt;&lt;span&gt;    { name } -&amp;gt; Person { name, age: 0 }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which is pretty cool! There are lots of things you can do, like defining a &lt;code&gt;Default&lt;&#x2F;code&gt; type that wraps unit, &lt;code&gt;()&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;type Default = ()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we can add a default implementation to person!&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;impl Person from Default 
&lt;&#x2F;span&gt;&lt;span&gt;    = Default -&amp;gt; Person {
&lt;&#x2F;span&gt;&lt;span&gt;        name: &amp;quot;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        age:  0,
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So now it’s possible to write:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Person Default
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or use row splicing to fill in a &lt;code&gt;Person&lt;&#x2F;code&gt; from the &lt;code&gt;Default&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;ug = Person {
&lt;&#x2F;span&gt;&lt;span&gt;    name: &amp;quot;ug&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    .. Person Default,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Where &lt;code&gt;.. Person Default&lt;&#x2F;code&gt; essentially means fill the rest of this struct from &lt;code&gt;Person Default&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;But I guess you could argue that &lt;code&gt;Person&lt;&#x2F;code&gt; isn’t really a trait. Don’t we have this backwards? Wouldn’t we want to &lt;code&gt;impl Default from Person&lt;&#x2F;code&gt;, whatever that means?&lt;&#x2F;p&gt;
&lt;p&gt;Well yes and no. Let’s look at some iterators.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;iterators-in-passerine&quot;&gt;Iterators in Passerine&lt;&#x2F;h1&gt;
&lt;p&gt;So Passerine has a little problem, and it’s twofold:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;It only supports inductive datatypes (i.e. anything you could trivially serialize to JSON, no cycles).&lt;&#x2F;li&gt;
&lt;li&gt;Closures can only capture immutable values.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;In Rust we defined our type representing an iterator using, well, a mutable closure:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Iterator&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;Item&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;next&lt;&#x2F;span&gt;&lt;span&gt;: Box&amp;lt;dyn FnMut() -&amp;gt; Option&amp;lt;Item&amp;gt;&amp;gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So to model &lt;code&gt;Iterator&lt;&#x2F;code&gt; in Passerine, we’d essentially have to make the internal state explicit:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;type Iterator = all State Item -&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    state: State,
&lt;&#x2F;span&gt;&lt;span&gt;    _next: State -&amp;gt; Option (State, Item)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which isn’t too bad, because we can define a function &lt;code&gt;next&lt;&#x2F;code&gt; that operates on iterators, instead of calling &lt;code&gt;iterator._next&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;next : all S I 
&lt;&#x2F;span&gt;&lt;span&gt;    -&amp;gt; (Iterator S I) 
&lt;&#x2F;span&gt;&lt;span&gt;    -&amp;gt; Option (Iterator S I, T)
&lt;&#x2F;span&gt;&lt;span&gt;= Iterator { state, _next } -&amp;gt; match (_next state) {
&lt;&#x2F;span&gt;&lt;span&gt;    Some (new_state, item) -&amp;gt; Some (
&lt;&#x2F;span&gt;&lt;span&gt;        Iterator { state: new_state, _next }, 
&lt;&#x2F;span&gt;&lt;span&gt;        item,
&lt;&#x2F;span&gt;&lt;span&gt;    ),
&lt;&#x2F;span&gt;&lt;span&gt;    None -&amp;gt; None,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Aside:&lt;&#x2F;strong&gt; In the future, it might be possible to model hidden state using Passerine’s effect system.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yeah, it kinda looks like a mess, and I invented some syntax, but bear with me.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s define an iterator, &lt;code&gt;Fib&lt;&#x2F;code&gt;, that we can use to calculate the fibonacci sequence:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;type Fib = ()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;impl Iter (Nat, Nat) Nat for Fib = Fib -&amp;gt; Iter {
&lt;&#x2F;span&gt;&lt;span&gt;    state: (0, 1),
&lt;&#x2F;span&gt;&lt;span&gt;    _next: (a, b) -&amp;gt; Some ((a + b, a), a),
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So our internal &lt;code&gt;State&lt;&#x2F;code&gt; is a pair of numbers &lt;code&gt;(Nat, Nat)&lt;&#x2F;code&gt;, and at each iteration we produce an &lt;code&gt;Item&lt;&#x2F;code&gt;, which is a number &lt;code&gt;Nat&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s say we define a function that takes an &lt;code&gt;Iter&lt;&#x2F;code&gt;, and prints out all its elements:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;println_all_iter = (iter: Iter) -&amp;gt; match (next iter) {
&lt;&#x2F;span&gt;&lt;span&gt;    None -&amp;gt; None,
&lt;&#x2F;span&gt;&lt;span&gt;    Some (new_iter, item) -&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        println item
&lt;&#x2F;span&gt;&lt;span&gt;        print_all_iter new_iter
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Printing out all the fibonacci numbers (warning!) is as easy as:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;println_all_iter (Iter (Fib ()))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which we can write using &lt;code&gt;|&amp;gt;&lt;&#x2F;code&gt; notation as follows:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Fib () |&amp;gt; Iter |&amp;gt; println_all_iter
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;some-sugar&quot;&gt;Some sugar!&lt;&#x2F;h1&gt;
&lt;p&gt;Converting a &lt;code&gt;Wizard&lt;&#x2F;code&gt; to a &lt;code&gt;Person&lt;&#x2F;code&gt; is all well and good, but what if we just want a &lt;code&gt;Wizard&lt;&#x2F;code&gt;’s age? Currently, you’d have to do something like:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;(Person merlin).age
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which isn’t that bad. But what if we made &lt;code&gt;Type.field&lt;&#x2F;code&gt; sugar for &lt;code&gt;object -&amp;gt; (Type object).field&lt;&#x2F;code&gt;? Then the above would be:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Person.age merlin
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;which is reminiscent of a uniform function call syntax (UFCS), used to disambiguate when multiple traits are present.&lt;&#x2F;p&gt;
&lt;p&gt;Speaking of multiple traits…&lt;&#x2F;p&gt;
&lt;h1 id=&quot;disambiguating-multiple-traits&quot;&gt;Disambiguating Multiple Traits&lt;&#x2F;h1&gt;
&lt;p&gt;Currently, our functions can really only operate on one trait at a time. If our function accepts an &lt;code&gt;Iterator&lt;&#x2F;code&gt;, we can’t also specify that that type also implements the trait &lt;code&gt;Length&lt;&#x2F;code&gt;. Let’s take a second to flesh out this line of reasoning.&lt;&#x2F;p&gt;
&lt;p&gt;So we have two traits:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;type Iterator = ... -- see previous definition
&lt;&#x2F;span&gt;&lt;span&gt;type Length   = Nat -- the length of a collection
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can implement both &lt;code&gt;Iterator&lt;&#x2F;code&gt; and &lt;code&gt;Length&lt;&#x2F;code&gt; from a list &lt;code&gt;[T]&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;all T -&amp;gt; impl Iterator Nat T from [T] 
&lt;&#x2F;span&gt;&lt;span&gt;= list -&amp;gt; Iterator {
&lt;&#x2F;span&gt;&lt;span&gt;    state: 0
&lt;&#x2F;span&gt;&lt;span&gt;    _next: index -&amp;gt; if (list.length == index) {
&lt;&#x2F;span&gt;&lt;span&gt;        None
&lt;&#x2F;span&gt;&lt;span&gt;    } else {
&lt;&#x2F;span&gt;&lt;span&gt;        Some (index + 1)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;all T -&amp;gt; impl Length from [T] = list -&amp;gt; list.length
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let’s say we’re writing a function that takes an &lt;code&gt;Iterator&lt;&#x2F;code&gt; it needs to know the &lt;code&gt;Length&lt;&#x2F;code&gt; of. We &lt;em&gt;could&lt;&#x2F;em&gt; require the function to take both separately:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;println_half = iter length -&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    for _ in (length &#x2F; 2) {
&lt;&#x2F;span&gt;&lt;span&gt;        (iter, item) = next iter |&amp;gt; unwrap
&lt;&#x2F;span&gt;&lt;span&gt;        println item
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But this is bad because there’s no &lt;em&gt;requirement&lt;&#x2F;em&gt; that &lt;code&gt;length&lt;&#x2F;code&gt; is actually the length of &lt;code&gt;iter&lt;&#x2F;code&gt;. If we pass in an incorrect length, we could cause &lt;code&gt;println_half&lt;&#x2F;code&gt; to &lt;code&gt;unwrap&lt;&#x2F;code&gt; a &lt;code&gt;None&lt;&#x2F;code&gt; value! Aaaaaah!&lt;&#x2F;p&gt;
&lt;p&gt;Ideally, we’d want to be able to specify that &lt;code&gt;iter&lt;&#x2F;code&gt; implements both &lt;code&gt;Iterator&lt;&#x2F;code&gt; and &lt;code&gt;Length&lt;&#x2F;code&gt;. Let’s start by writing out the type of &lt;code&gt;println_half&lt;&#x2F;code&gt; as-is:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;println_half : all S I 
&lt;&#x2F;span&gt;&lt;span&gt;    -&amp;gt; Iterator S I 
&lt;&#x2F;span&gt;&lt;span&gt;    -&amp;gt; Nat 
&lt;&#x2F;span&gt;&lt;span&gt;    -&amp;gt; ()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;One step we could take is just passing &lt;code&gt;iter&lt;&#x2F;code&gt; to &lt;code&gt;println_half&lt;&#x2F;code&gt; &lt;em&gt;twice&lt;&#x2F;em&gt;, and then constructing &lt;code&gt;Length&lt;&#x2F;code&gt; and &lt;code&gt;Iterator&lt;&#x2F;code&gt; once inside the function:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;println_half = list -&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    iter   = Iter list
&lt;&#x2F;span&gt;&lt;span&gt;    length = Length list
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    for _ in (length &#x2F; 2) {
&lt;&#x2F;span&gt;&lt;span&gt;        (iter, item) = next iter |&amp;gt; unwrap
&lt;&#x2F;span&gt;&lt;span&gt;        println item
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If we were to write this as a type, we’d need some way to say that &lt;code&gt;iter&lt;&#x2F;code&gt; implements &lt;em&gt;both&lt;&#x2F;em&gt; &lt;code&gt;Iterator&lt;&#x2F;code&gt; and &lt;code&gt;Length&lt;&#x2F;code&gt;. What would this type &lt;code&gt;???&lt;&#x2F;code&gt; be?&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;println_half : all S I
&lt;&#x2F;span&gt;&lt;span&gt;    -&amp;gt; ???
&lt;&#x2F;span&gt;&lt;span&gt;    -&amp;gt; ()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Rust already has a solution for this; it’s to use &lt;code&gt;+&lt;&#x2F;code&gt; to constrain a generic type to a trait:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;println_half&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T, U: Iterator&amp;lt;Item=T&amp;gt; + Length&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span&gt;: U) -&amp;gt; { ... }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here &lt;code&gt;U&lt;&#x2F;code&gt; represents a type that is both an &lt;code&gt;Iterator&lt;&#x2F;code&gt; and has a &lt;code&gt;Length&lt;&#x2F;code&gt;. I feel like we could adopt something like this for Passerine wholesale, but as Passerine’s generic story isn’t that strong yet (heck, I haven’t even decided on a syntax!), I wouldn’t want to overstretch the language in this manner.&lt;&#x2F;p&gt;
&lt;p&gt;One thing I have been working on, though, are type sets, in relation to Passerine’s effect system.&lt;&#x2F;p&gt;
&lt;p&gt;I’m not going to go into too much depth here, but for those familiar, under an effect system, functions may produce a set of side effects:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;print_random_number: () -&amp;gt; {Console, Random} () {
&lt;&#x2F;span&gt;&lt;span&gt;    random_number ()
&lt;&#x2F;span&gt;&lt;span&gt;    |&amp;gt; to_string
&lt;&#x2F;span&gt;&lt;span&gt;    |&amp;gt; println
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So &lt;code&gt;{Console, Random}&lt;&#x2F;code&gt; represents what side effects &lt;code&gt;print_random_number&lt;&#x2F;code&gt; causes. Because this is an effect &lt;em&gt;set&lt;&#x2F;em&gt;, &lt;code&gt;{A, B}&lt;&#x2F;code&gt;, is equivalent to &lt;code&gt;{B, A}&lt;&#x2F;code&gt;, and so on.&lt;&#x2F;p&gt;
&lt;p&gt;A &lt;em&gt;type set&lt;&#x2F;em&gt; could be a generalization of this to types. It’s the set of possible types another type implements as a trait. The type set of something that is iterable and has a length is:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;{Iterator, Length}
&lt;&#x2F;span&gt;&lt;span&gt;-- leaving out the generics
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which means we can treat anything of that type as both an &lt;code&gt;Iterator&lt;&#x2F;code&gt; and a &lt;code&gt;Length&lt;&#x2F;code&gt;, using a UFCS-like syntax (sugar) to disambiguate when necessary, as shown above.&lt;&#x2F;p&gt;
&lt;p&gt;So we could write the type of this updated &lt;code&gt;println_half&lt;&#x2F;code&gt; as follows:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;println_half : all S I
&lt;&#x2F;span&gt;&lt;span&gt;    -&amp;gt; {Iterator S I, Length}
&lt;&#x2F;span&gt;&lt;span&gt;    -&amp;gt; ()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which I think is fairly clean.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;something-deeper&quot;&gt;Something deeper?&lt;&#x2F;h1&gt;
&lt;p&gt;I think that this relationship between traits and algebraic effects is interesting. It’s something I’ve discussed with others in the past, and it’s something I’d like to continue to explore in the future.&lt;&#x2F;p&gt;
&lt;p&gt;When you think about it, effects are really just dynamically scoped traits; traits whose implementations change depending on dynamic, as opposed to lexically-resolved nominal, scope.&lt;&#x2F;p&gt;
&lt;p&gt;If Rust implemented algebraic effects, would they use &lt;code&gt;+&lt;&#x2F;code&gt; (as discussed earlier) and look like, well, traits? Makes me wonder…&lt;&#x2F;p&gt;
&lt;p&gt;I feel like there’s something deeper here. In essence, our trait system has boiled down to dynamic dispatch over conversion between types.&lt;&#x2F;p&gt;
&lt;p&gt;If I wanted a real trait system, I’d probably adopt HKTs and typeclasses. But part of designing a programming language is choosing a limiting set of axioms, and I think that having a separate language for type-level programming goes against Passerine’s design, which boils down to a functional scripting language. I’m already worried that typechecking will make Passerine take too long to compile; my goal is to have startup times as fast as something like Python.&lt;&#x2F;p&gt;
&lt;p&gt;I know that what I’ve been getting at — representing typeclasses as explicit datastructures — is nothing new, and has been common in both languages without higher-kinded types (such as F#, to get around limitations in the language), and languages with higher-kinded types (such as Agda, where types are just objects, so why couldn’t they be represented as ‘plain datastructures’?).&lt;&#x2F;p&gt;
&lt;p&gt;I feel like all languages in this area are slowly tending towards Agda. Then again, a while back it seemed like everything tended towards Scheme, so maybe it’s just a matter of perspective.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, I digress. I hope you found this little post interesting, thanks for reading!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;one-last-note&quot;&gt;One last note&lt;&#x2F;h1&gt;
&lt;p&gt;I’ve noticed something interesting. When dealing with closed enumerations, we allow users of that closed enumeration to see any of the members that constitute that enumeration:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;enum &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;TrafficLight &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    Red,
&lt;&#x2F;span&gt;&lt;span&gt;    Yellow,
&lt;&#x2F;span&gt;&lt;span&gt;    Green,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When we match of traffic light, we can be sure to handle every pattern:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;Rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-Rust &quot;&gt;&lt;code class=&quot;language-Rust&quot; data-lang=&quot;Rust&quot;&gt;&lt;span&gt;use TrafficLight::*;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;match &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        Red =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;red&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        Yellow =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;yellow&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        Green =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;green&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    }.to_string()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I want you to stop for a second an just realize that each match branch is a bit like a function. For example, the first branch takes an object of type &lt;code&gt;TrafficLight::Red&lt;&#x2F;code&gt; and returns a static string (&lt;code&gt;&amp;amp;&#x27;static str&lt;&#x2F;code&gt;). We could write this type out as:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;TrafficLight::Red -&amp;gt; &amp;amp;&amp;#39;static str
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So each match branch is really like a function, a closure. “Take the type that matches this pattern, produce this result”. Note that all match arms produce a result of the same type, so a &lt;code&gt;match&lt;&#x2F;code&gt; expression is a bit like a fan-out that compresses each possible branch into a single value.&lt;&#x2F;p&gt;
&lt;p&gt;So why do I bring this up now? Well, when you’re using an open enumeration, like a trait, &lt;em&gt;you can’t possibly know all the types that a value could be&lt;&#x2F;em&gt;. Behind the scenes, though, there’s still a massive match expression.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;&#x2F;&#x2F; `Named` trait
&lt;&#x2F;span&gt;&lt;span&gt;match type {
&lt;&#x2F;span&gt;&lt;span&gt;    Person =&amp;gt; ...
&lt;&#x2F;span&gt;&lt;span&gt;    Wizard =&amp;gt; ...
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So when we implement a trait for yet another type, we’re really just adding another branch to the behind-the-scenes match expression. For example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;Rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-Rust &quot;&gt;&lt;code class=&quot;language-Rust&quot; data-lang=&quot;Rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Named for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;TrafficLight &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; String {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The method &lt;code&gt;name&lt;&#x2F;code&gt; is just a function of type &lt;code&gt;TrafficLight -&amp;gt; String&lt;&#x2F;code&gt;. Note the parallels here!&lt;&#x2F;p&gt;
&lt;p&gt;Under closed enumeration, we declare all the potential types (i.e. variants) up-front, and then match on these variants to extract common structure&#x2F;behavior:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; infinite number of behaviors
&lt;&#x2F;span&gt;&lt;span&gt;behavior = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; Closed {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; finite number of variants
&lt;&#x2F;span&gt;&lt;span&gt;    Variant -&amp;gt; Dispatch,
&lt;&#x2F;span&gt;&lt;span&gt;    Variant -&amp;gt; Dispatch,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But under an open enumeration, there are possibly an infinite number of variants! So we declare all possible behaviors up front:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; infinite number of variants
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;trait &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Open &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; finite number of behaviors
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;behavior&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; Dispatch;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Whenever we want to add a &lt;code&gt;Variant&lt;&#x2F;code&gt; to &lt;code&gt;Open&lt;&#x2F;code&gt;, we have to provide a match arm for each ‘behind-the-scenes’ &lt;code&gt;behavior&lt;&#x2F;code&gt; match expression:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;Rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-Rust &quot;&gt;&lt;code class=&quot;language-Rust&quot; data-lang=&quot;Rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Open for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Variant &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; This is just a match arm!
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;behavior&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; Dispatch { 
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So for each &lt;code&gt;behavior&lt;&#x2F;code&gt; in the &lt;code&gt;Open&lt;&#x2F;code&gt; enumeration, we provide a match arm: &lt;code&gt;Variant -&amp;gt; Dispatch&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The compiler stitches all these disparate match arms together to form these behind the scene match expressions:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; finite number of behaviors
&lt;&#x2F;span&gt;&lt;span&gt;behavior = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; Open {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; infinite number of variants
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Open for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;Variant&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;impl Open for Variant&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each implementation is like an opaque match arm.&lt;&#x2F;p&gt;
&lt;p&gt;To summarize:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Under closed enumerations, we have a finite number of variant branches; we must handle each branch while being able to implement arbitrarily many behaviors.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Under open enumerations, we have an infinite number of possible variant branches; to add a new variant, we must provide the ‘match-arms’ for a finite number of behaviors.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Now because open enumerations are defined around a finite set of behaviors, it makes sense that each variant &lt;em&gt;must&lt;&#x2F;em&gt; provide a function.&lt;&#x2F;p&gt;
&lt;p&gt;It is simply not possible to have both an infinite number of behaviors and an infinite number of variants. You can pick one or the other, and the structure of the resulting code will be affected by that decision.&lt;&#x2F;p&gt;
&lt;p&gt;I just love this parallel, and think that it drastically simplified my mental model of traits vs enums.&lt;&#x2F;p&gt;
&lt;p&gt;Traits as implicit conversion is essentially makes types open enumerations over the behavior of their constructor function. This model is really elegant because it reifies types and traits (we’re not ‘adding an extra layer’ to the language), but on the other hand it does complicate things somewhat.&lt;&#x2F;p&gt;
&lt;p&gt;I think this is honestly the pain of being a language designer. You get a &lt;em&gt;feeling&lt;&#x2F;em&gt; that there are these fundamental underlying constructs that underpin the way the world works. You spend a lot of time refining these feelings—writing them down, building prototypes—only to realize that everything old is new again, or that there are new cases you haven’t thought about that don’t neatly fit your model.&lt;&#x2F;p&gt;
&lt;p&gt;I wish there was just &lt;em&gt;a&lt;&#x2F;em&gt; language, a silver bullet, where these sorts of tradeoffs didn’t exist. A language where there was one single &lt;em&gt;obvious&lt;&#x2F;em&gt; way to implement something. A language where all semantic symmetries were wrapped up in symmetrical syntax, all constructs discovered through intuitive exploration and composition.&lt;&#x2F;p&gt;
&lt;p&gt;I know that this youthful idealism is unwarranted. The rubber has to hit the road somewhere, tradeoffs will always exist, and software is never developed alone. We need a Go of functional programming, whatever what that ends up looking like: a smaller Rust, a minimal Agda, a typed Scheme.&lt;&#x2F;p&gt;
&lt;p&gt;I can’t claim that Passerine will be that language. I’ve worked hard to engineer a minimal set of orthogonal features that &lt;em&gt;compose&lt;&#x2F;em&gt;. Once I’ve figured out how to unify effects and fibers (it’s mostly a matter of notation at this point), and have more cleanly delineated the line between the macro system and the type system, I think I may have an unstoppable seed of a language on my hands.&lt;&#x2F;p&gt;
&lt;p&gt;We’ll see where it goes from here :)&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Sketchnoting: a spontaneous but needed reflection</title>
        <published>2022-02-11T00:00:00+00:00</published>
        <updated>2022-02-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/note/"/>
        <id>https://slightknack.dev/blog/note/</id>
        
        <summary type="html">&lt;h1 id=&quot;preface&quot;&gt;Preface&lt;&#x2F;h1&gt;
&lt;p&gt;Since September of last year, I’ve been chipping away at a long-form post on how to take &lt;em&gt;good&lt;&#x2F;em&gt; notes. This, sadly, is not that post—consider this post a teaser to tide you over until then.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;content&#x2F;note-vols.jpg&quot; alt=&quot;Four filled notebooks and one new empty one.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Today I finished my fourth book of sketchnotes. Each book has about 240 blank A5 pages, so I guess I’m fast-approaching the thousand-page mark. In celebration of filling yet another volume, I took a trip down memory lane and dug out &lt;em&gt;Volumes 1-3&lt;&#x2F;em&gt;. Taking the time to read through some of my older notes, it’s easy to see that I’ve improved quite a lot.&lt;&#x2F;p&gt;
&lt;p&gt;While reading through my second notebook, though, I found a short hand-written collection of my thoughts on note-taking. Although it’s been a few years since then, I feel like the core of what I had to say then rings true today.&lt;&#x2F;p&gt;
&lt;p&gt;Below I’ve typed up those few pages of notes, I hope you find them interesting:&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Predicting the technology of 2027</title>
        <published>2022-01-04T00:00:00+00:00</published>
        <updated>2022-01-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/2027/"/>
        <id>https://slightknack.dev/blog/2027/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/2027/">&lt;p&gt;The other day, I was thinking about this question:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;What’s something that is not common today that you think will be really common 5 years from now?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;As I do a lot of programming language design, I can’t help but think about what our tooling will be like in 5 years. I’m writing this in 2022, so here are some of my predictions for 2027, in no particular order:&lt;&#x2F;p&gt;
&lt;h1 id=&quot;copilot-co&quot;&gt;Copilot &amp;amp; co.&lt;&#x2F;h1&gt;
&lt;p&gt;First things first, although I strongly dislike (and don’t use) copilot, I think that AI-based code completion tools will become more and more common to the point of being indispensable. Recent advances in large language models reduce the attention complexity from &lt;code&gt;n²&lt;&#x2F;code&gt; to &lt;code&gt;n&lt;&#x2F;code&gt;, which means we’ll be seeing larger attention windows, and hence more concise code generation.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, work from Google on query-based models (models that have an external database) will be put to use, allowing for correct attribution as to where the model is generating its completion from, more up-to-date completions (model doesn’t have to be retrained, only database has to be updated), and more specific completions (using current codebase as database).&lt;&#x2F;p&gt;
&lt;h1 id=&quot;structured-tooling&quot;&gt;Structured Tooling&lt;&#x2F;h1&gt;
&lt;p&gt;With the rise of JetBrains, Tree-Sitter, and languages like Unison, I believe we’ll see more structured tooling; instead of modifying text, we’ll be working on entire ASTs. editors already do this to some degree, with completion, automatic bracket insertion, and indentation preservation, but I think tools for automatic semantic refactoring will become more prevalent. For this to happen I estimate that either a new plugin, editor, or LSP will be developed with support for all major languages.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;effects&quot;&gt;Effects&lt;&#x2F;h1&gt;
&lt;p&gt;Language-wise, there are some exciting features I see coming soon to a Language Near You™. The first one being a move towards algebraic effects for effect modeling. It turns out that a lot of language features—like control flow, concurrency, async&#x2F;await, yielding, exceptions, etc.—can be modeled in terms of effects. There’s a lot of potential here, and I think that either through libraries or (hopefully) language-level support, they’ll become more prevalent.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;gradual-typing&quot;&gt;Gradual Typing&lt;&#x2F;h1&gt;
&lt;p&gt;Noting the rise of TypeScript, I think we’ll also see the move towards languages that allow people to specify correctness in their programs over time. Functions can be written purely dynamically, like python, but over time type annotations can be added until you end up with strongly-typed programs a la Haskell.&lt;&#x2F;p&gt;
&lt;p&gt;Once a program is fully typed, there may be additional tools to prove the correctness of parts of a program. I don’t think this will be mainstream, but I think within 5 years tools like this will become more common and more ergonomic than they are today.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;machine-reinforcement-learning&quot;&gt;Machine&#x2F;Reinforcement Learning&lt;&#x2F;h1&gt;
&lt;p&gt;Aside from languages and tooling, I’d like to make some predictions within the field of ML. I predict we’ll see the creation of a language model that achieves above-human-expert performance on a number of text-based tasks. Some will say AI has passed human intelligence, others will say it’s only expert in a narrow context. Either way, they’re both forgetting that ‘AI’ will always be whatever humans &lt;em&gt;can&lt;&#x2F;em&gt; do that machines can &lt;em&gt;not&lt;&#x2F;em&gt; do yet.&lt;&#x2F;p&gt;
&lt;p&gt;I also think we’ll see the rise of more accurate and less data-intensive RL techniques that rely on attention-based trajectory prediction rather than PPO-like policy optimization. TL;DR is that we’ll become better at training models on tasks with low data availability for general optimization. Sadly, I think ML is moving away from open-source, and I think that most production models will be released as a blogpost + API instead of paper + code.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;games-raytracing-vr&quot;&gt;Games, Raytracing, VR&lt;&#x2F;h1&gt;
&lt;p&gt;With respect to games and graphics, real-time raytracing will become the norm. Aside from the increase in realism in games, as photorealism becomes more realistic, we’ll also see the move towards selective and stylistic games that use raytracing and PBR techniques to strongly enforce a non-PBR-like style. Real-time style transfer at high-resolutions may play an important role in this.&lt;&#x2F;p&gt;
&lt;p&gt;Oculus outsold XBox this year, which means that VR will become a lot bigger; your favorite VR game probably hasn’t been created yet. I generally dislike VR, but the technology is becoming a lot better. I just pray that the zuckerbergian dystopian metaverse doesn’t come to fruition and is usurped by a wider open-source protocol.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;&#x2F;h1&gt;
&lt;p&gt;I tried to be both optimistic yet honest in my above predictions. I don’t think there’s anything too surprising there for anyone entrenched in the field, but I’d really like to see how spot-on I was 5 years from now. Hey, maybe this’ll be a self-fulfilling prophecy.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Using Proof-of-Work to Manage Backpressure</title>
        <published>2021-12-01T00:00:00+00:00</published>
        <updated>2021-12-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/dynamic-pow-backpressure/"/>
        <id>https://slightknack.dev/blog/dynamic-pow-backpressure/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/dynamic-pow-backpressure/">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; This is a quick piece that assumes some prior knowledge of PoW and backpressure. If you want to build up an intuition about backpressure before jumping in, you could read &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@jayphelps&#x2F;backpressure-explained-the-flow-of-data-through-software-2350b3e77ce7&quot;&gt;&lt;em&gt;this piece&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.tedinski.com&#x2F;2019&#x2F;03&#x2F;05&#x2F;backpressure.html&quot;&gt;&lt;em&gt;that one&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;. If you’d like to learn more about PoW in the context of this article, check out &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Hashcash&quot;&gt;&lt;em&gt;this article&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; (and &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Proof_of_work&quot;&gt;&lt;em&gt;that one as well&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;On the bus this afternoon, I was reading up on backpressure in distributed systems. Backpressure, long story short, is the backward force acting on data as it moves through a system.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;what-s-backpressure&quot;&gt;What’s Backpressure?&lt;&#x2F;h1&gt;
&lt;p&gt;Maintaining a healthy level of backpressure is important for creating a well-running system monolithic, microservice, distributed, or otherwise. Too little backpressure and services will be spending time idle waiting for work to arrive. Too much backpressure, on the other hand, can lead to a buildup of work and fatal system resets.&lt;&#x2F;p&gt;
&lt;p&gt;One of the first mistakes I made when first building distributed systems was not paying attention to the backpressure bottlenecks of the system. Although there are many ways to classify, manage, and build systems that main consistent backpressure, one system I want to focus on is proof-of-work based rate limiting.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;proof-of-work&quot;&gt;Proof of Work&lt;&#x2F;h1&gt;
&lt;p&gt;Proof-of-work (PoW) is nothing new. Now primarily associated with consensus mechanisms used BitCoin et al., PoW was first used an obscure email tool used to rate-limit spam and DoS attacks. In no small part due to the popularity of BitCoin, PoW has found a number of other uses.&lt;&#x2F;p&gt;
&lt;p&gt;I was prompted to write this post after re-reading the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;pest.bitdash.io&#x2F;whitepaper.html&quot;&gt;Pest&lt;&#x2F;a&gt; specification, a distributed IRC-like protocol that uses PoW to maintain a steady influx of messages.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;managing-backpressure&quot;&gt;Managing Backpressure&lt;&#x2F;h1&gt;
&lt;p&gt;When managing backpressure, there are essentially two things that can be going wrong:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;You’re not receiving enough messages, meaning there’s probably a &lt;em&gt;bottleneck&lt;&#x2F;em&gt; somewhere else in the system.&lt;&#x2F;li&gt;
&lt;li&gt;You’re receiving too many messages, meaning &lt;em&gt;you’re&lt;&#x2F;em&gt; probably the bottleneck in the system.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;It’s important to remember that not all messages sent in a distributed system are necessary for the healthy operation of the system. When a service gets too stressed, services calling that service can reduce the number of messages they’re sending to improve the latency of the system (while maintaining consistent throughput).&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pow-and-backpressure&quot;&gt;PoW and Backpressure&lt;&#x2F;h1&gt;
&lt;p&gt;The PoW Backpressure scheme I propose is pretty simple. Whenever a node—let’s call it the &lt;em&gt;client&lt;&#x2F;em&gt;—establishes a connection with a service it relies on—the &lt;em&gt;server&lt;&#x2F;em&gt;—the server returns a nonce and a work level for use in the next request. We’ll call this the &lt;em&gt;challenge&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;nonce&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;C9B76FBD&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;work&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To make a request, the client must complete the challenge. To do so, it must calculate the hash of the request it wants to send, append the nonce, and compute &lt;code&gt;N&lt;&#x2F;code&gt; rounds of proof of work (e.g. repeated hashing) until the specified work level has been met. The response and corresponding &lt;em&gt;proof&lt;&#x2F;em&gt; are then sent to the server for processing:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;nonce&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;C9B76FBD&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;456&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;req&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:   { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;PoW proofs are cheap to verify. If the server tries to validate the PoW and finds it to be invalid (incorrect &lt;code&gt;nonce&lt;&#x2F;code&gt; or incorrect &lt;code&gt;N&lt;&#x2F;code&gt;), it simply drops the message. If the proof is valid, however, the server unwraps the inner request &lt;code&gt;req&lt;&#x2F;code&gt; from the client and processes it as usual.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Aside:&lt;&#x2F;strong&gt; Whenever the server receives a message, it should &lt;em&gt;immediately&lt;&#x2F;em&gt; send a new challenge to the client.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Proof-of-work acts as a natural rate limiter of requests. It requires the client to do the bulk of the work creating a valid request. This is especially important in the context of untrusted distributed contexts, where it is easy to increase the work attackers need to perform without impacting the work the server needs to do to validate correctly formed packets.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;adjusting-the-rate-limit&quot;&gt;Adjusting the rate limit&lt;&#x2F;h1&gt;
&lt;p&gt;In the last section, we know how PoW can act as a rate limit. In this section I want to talk about adjusting that rate limit.&lt;&#x2F;p&gt;
&lt;p&gt;When the backpressure is too low, the work limit should be decreased; when it is to high, it should be increased. Say we have a queue of unprocessed requests &lt;code&gt;B&lt;&#x2F;code&gt;. We can set the backpressure as a function of the length of &lt;code&gt;B&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;Each successive work factor is twice as hard as the previous one: &lt;code&gt;2&lt;&#x2F;code&gt; is twice as much work as &lt;code&gt;1&lt;&#x2F;code&gt;, just as &lt;code&gt;11&lt;&#x2F;code&gt; is twice as much work as &lt;code&gt;10&lt;&#x2F;code&gt;. We can quantify the rate of incoming messages as:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;incoming &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;network_capacity &lt;&#x2F;span&gt;&lt;span&gt;* (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;^ -&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;work_factor&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So at a rate of &lt;code&gt;1000&lt;&#x2F;code&gt; messages per second when the work factor is &lt;code&gt;0&lt;&#x2F;code&gt;, changing the work factor to &lt;code&gt;3&lt;&#x2F;code&gt; would change the rate of incoming messages to &lt;code&gt;1000 * (1&#x2F;8)&lt;&#x2F;code&gt;, or &lt;code&gt;125&lt;&#x2F;code&gt;, messages per second.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s say we want to maintain a backpressure of &lt;code&gt;K&lt;&#x2F;code&gt; items in queue &lt;code&gt;B&lt;&#x2F;code&gt;, out of the length of our queue, &lt;code&gt;len(B)&lt;&#x2F;code&gt;. We know we can send &lt;code&gt;outgoing&lt;&#x2F;code&gt; messages per second, and can estimate the network capacity given the current work factor. To calculate the work factor we want to set, we could use something like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;work_factor &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;log_2&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;network_capacity &lt;&#x2F;span&gt;&lt;span&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;outgoing&lt;&#x2F;span&gt;&lt;span&gt;) * (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;B&lt;&#x2F;span&gt;&lt;span&gt;) &#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;K&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This equation will try to match the throughput of the input, times some adjustment used to maintain constant backpressure. Let’s try out an example.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s say we’re receiving &lt;code&gt;1000&lt;&#x2F;code&gt; messages per second, we want &lt;code&gt;100&lt;&#x2F;code&gt; in queue, and we process &lt;code&gt;200&lt;&#x2F;code&gt; per second. Our queue currently has &lt;code&gt;800&lt;&#x2F;code&gt; messages in it. What should we set our work factor to be?&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;work_factor &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;log_2&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;network_capacity &lt;&#x2F;span&gt;&lt;span&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;outgoing&lt;&#x2F;span&gt;&lt;span&gt;) * (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;B&lt;&#x2F;span&gt;&lt;span&gt;) &#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;K&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;work_factor &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;log_2&lt;&#x2F;span&gt;&lt;span&gt;((            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1000 &lt;&#x2F;span&gt;&lt;span&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;200     &lt;&#x2F;span&gt;&lt;span&gt;) * (   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;800 &lt;&#x2F;span&gt;&lt;span&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;work_factor &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;log_2&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;40&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;work_factor &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;5.32
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;work_factor &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;5 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; rounded
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once the target backpressure is restored, this work factor will decrease until a steady state is reached. Once the state is reached (i.e. &lt;code&gt;len(B) == K&lt;&#x2F;code&gt;), in the above example, the work factor will decrease to &lt;code&gt;2&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;work_factor &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;log_2&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;1000 &lt;&#x2F;span&gt;&lt;span&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;) * (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;100 &lt;&#x2F;span&gt;&lt;span&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;)) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; len(B) == K
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;work_factor &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;log_2&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;work_factor &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2.32
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;work_factor &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; rounded
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This work factor is quick to calculate and will adjust to automatically. For &lt;em&gt;servers&lt;&#x2F;em&gt; that can scale horizontally, it can also be a good metric as to when to spin up more instances (if you want to maintain low latency without rate limiting your users).&lt;&#x2F;p&gt;
&lt;h1 id=&quot;dynamic-rate-limiting&quot;&gt;Dynamic Rate Limiting&lt;&#x2F;h1&gt;
&lt;p&gt;There is nothing that requires that PoW be universal across all clients. Servers can be selective and choose to increase work for IPs exhibiting DoS-like behavior, clients sending too many messages, or untrusted clients in a distributed network.&lt;&#x2F;p&gt;
&lt;p&gt;There’s a lot more that can be explored now, but it’s getting late and I value my sleep. :P&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;I hope you’ve found this little piece interesting. If you’re ever dealing with managing backpressure, I hope that you’ll find PoW-based rate limiting to be a useful tool in your toolbox. Until next time!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Thinking about efficient backing stores for CRDTs</title>
        <published>2021-08-02T00:00:00+00:00</published>
        <updated>2021-08-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/backing-crdt-store/"/>
        <id>https://slightknack.dev/blog/backing-crdt-store/</id>
        
        <summary type="html">&lt;p&gt;A &lt;em&gt;Conflict-Free Replicated Datatype&lt;&#x2F;em&gt; is a bit like a smoothie: the same ingredients will produce the same result, regardless of the order in which they are added. In the context of, say, text editing in a distributed context, merging two documents will always succeed in a deterministic manner. In other words, A CRDT is a bit like a git repository that never has merge conflicts.&lt;&#x2F;p&gt;
&lt;p&gt;There are many different ways to approach the construction of CRDTs, each construction having its own strengths and weaknesses. Today, we’re going to focus on creating out an efficient backing store for a particular family of algorithms known as &lt;em&gt;Replicated Growth Arrays&lt;&#x2F;em&gt; (RGA).&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Getting bindgen working on NixOS</title>
        <published>2021-07-08T00:00:00+00:00</published>
        <updated>2021-07-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/nix-os-bindgen/"/>
        <id>https://slightknack.dev/blog/nix-os-bindgen/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/nix-os-bindgen/">&lt;blockquote&gt;
&lt;h1 id=&quot;note&quot;&gt;Note&lt;&#x2F;h1&gt;
&lt;p&gt;this is a collection of a set of discord messages sent on the NixOS discord server.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;troubles-w-nixos&quot;&gt;Troubles w&#x2F; NixOS&lt;&#x2F;h1&gt;
&lt;p&gt;I’m back! This is a hard one, I swear, and I’ve been banging my head against it for hours: I’m trying to compile a rust library that wraps &lt;code&gt;libcec&lt;&#x2F;code&gt;. To wrap &lt;code&gt;libc&lt;&#x2F;code&gt;, this library uses &lt;code&gt;bindgen&lt;&#x2F;code&gt;. Here’s what the crate roughly looks like:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;.
&lt;&#x2F;span&gt;&lt;span&gt;├── build.rs
&lt;&#x2F;span&gt;&lt;span&gt;├── Cargo.toml
&lt;&#x2F;span&gt;&lt;span&gt;└── src
&lt;&#x2F;span&gt;&lt;span&gt;    ├── lib.rs
&lt;&#x2F;span&gt;&lt;span&gt;    └── wrapper.h
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Two important things to note: &lt;code&gt;build.rs&lt;&#x2F;code&gt; invokes bindgen, doing something like the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;bindgen::Builder::default()
&lt;&#x2F;span&gt;&lt;span&gt;    .header(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;src&#x2F;wrapper.h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;    .generate()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And &lt;code&gt;wrapper.h&lt;&#x2F;code&gt; is just a simple header that includes &lt;code&gt;libcec&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;#include &amp;lt;libcec&#x2F;cecc.h&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On most operating systems, clang or whatever leafs through well-known &lt;code&gt;include&lt;&#x2F;code&gt; locations to toss together a big ol’ binary salad. NixOS, of course, loathes this. So, upon trying to compile this crate via &lt;code&gt;cargo build&lt;&#x2F;code&gt;, we get an error, sensibly enough:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;src&#x2F;wrapper.h:1:10: fatal error: &amp;#39;libcec&#x2F;cecc.h&amp;#39; file not found
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;No problemo! We can use &lt;code&gt;.clang_arg(...)&lt;&#x2F;code&gt; to send clang some args about where to look for &lt;code&gt;include&lt;&#x2F;code&gt; locations. Here’s an example that works with macOS, say:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;bindgen::Builder::default()
&lt;&#x2F;span&gt;&lt;span&gt;    .header(&amp;quot;src&#x2F;wrapper.h&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;    .clang_arg(&amp;quot;-I&#x2F;usr&#x2F;local&#x2F;include&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;    ...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So we just need to find the include location for &lt;code&gt;libcec&lt;&#x2F;code&gt; on NixOS, right? tossing this into our &lt;code&gt;shell.nix&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;nix&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-nix &quot;&gt;&lt;code class=&quot;language-nix&quot; data-lang=&quot;nix&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;# ...
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;LIBCEC_PATH&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;libcec&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;}&#x2F;include&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We then &lt;em&gt;should&lt;&#x2F;em&gt; be able to specify the right include location:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;.clang_arg(&amp;quot;-I$LIBCEC_PATH&amp;quot;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Huh, this doesn’t work, same error as before:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;src&#x2F;wrapper.h:1:10: fatal error: &amp;#39;libcec&#x2F;cecc.h&amp;#39; file not found
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Well, we can always just skip the wrapper and pull in the header file directly, right? Let’s echo &lt;code&gt;$LIBCEC_PATH&lt;&#x2F;code&gt; to find where in the heavens above &lt;code&gt;libcec&lt;&#x2F;code&gt; is, locate the header file, then schloop that in:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;$ tree $LIBCEC_PATH
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;nix&#x2F;store&#x2F;klsqc20n71gja5b8sa9ncw1jl6lcaxw9-libcec-6.0.2&#x2F;include
&lt;&#x2F;span&gt;&lt;span&gt;└── libcec
&lt;&#x2F;span&gt;&lt;span&gt;    ├── cecc.h
&lt;&#x2F;span&gt;&lt;span&gt;    └── ...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There it is!&lt;&#x2F;p&gt;
&lt;p&gt;I know this isn’t best practice, but let’s use this as the path to the header file we want in &lt;code&gt;build.rs&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;bindgen::Builder::default()
&lt;&#x2F;span&gt;&lt;span&gt;    .header(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&#x2F;nix&#x2F;store&#x2F;klsqc20n71gja5b8sa9ncw1jl6lcaxw9-libcec-6.0.2&#x2F;include&#x2F;libcec&#x2F;cecc.h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It’s not the prettiest, but it should work, right? … uh… right… ?&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;$ cargo build
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;nix&#x2F;store&#x2F;klsqc20n71gja5b8sa9ncw1jl6lcaxw9-libcec-6.0.2&#x2F;include&#x2F;libcec&#x2F;cectypes.h:38:10:
&lt;&#x2F;span&gt;&lt;span&gt;fatal error: &amp;#39;stdint.h&amp;#39; file not found
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Drat! Foiled again! It seems NixOS is too clever for me. This time the entirety of &lt;code&gt;libc&lt;&#x2F;code&gt; has gone missing!&lt;&#x2F;p&gt;
&lt;p&gt;At this point, I spent about another few hours trying to get &lt;code&gt;libc&lt;&#x2F;code&gt; to show up. I also read a ton (e.g. &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;C&quot;&gt;C on Nix wiki&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;gnulib&#x2F;manual&#x2F;html_node&#x2F;stdint_002eh.html&quot;&gt;stdint in gnulibc manual&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;issues?q=is%3Aissue+stdint.h+&quot;&gt;NixOS issue related to stdint&lt;&#x2F;a&gt;, etc.) to no avail.&lt;&#x2F;p&gt;
&lt;p&gt;So here’s my question: What am I doing wrong? How can I use &lt;code&gt;bindgen&lt;&#x2F;code&gt; to wrap a c library in a crate? If you need any additional information, ping me and I’ll send it right over! Thank you!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;update-an-incredible-solution&quot;&gt;Update! An incredible solution&lt;&#x2F;h1&gt;
&lt;p&gt;Shortly after I posted this, I reached out to &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Siraben&quot;&gt;Ben Siraphob&lt;&#x2F;a&gt;, a good friend of mine who convinced me to try out NixOS in the first place. We hopped on a call together, and he showed me how to reach a solution. Credit for the solution goes to him, I was in way over my head!&lt;&#x2F;p&gt;
&lt;p&gt;So, what was the solution, anyway? After searching for similar packages on Nixpkgs, we found that this issue itself was pretty uncommon (go figure). Luckily enough for us, it looked like the derivations of Firefox (I think?) and about two other projects had to deal with linking against evasive c library headers.&lt;&#x2F;p&gt;
&lt;p&gt;The first order of business was switching out &lt;code&gt;shell.nix&lt;&#x2F;code&gt; for a proper derivation. because it’s a Rust project we’re compiling, it’s best to use &lt;code&gt;rustPlatform.buildRustPackage&lt;&#x2F;code&gt;. After declaring the package and including it’s SHA, the first thing we needed to do was make sure &lt;code&gt;LIBCLANG&lt;&#x2F;code&gt; was in the right spot and could be found. This is simple enough:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;nix&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-nix &quot;&gt;&lt;code class=&quot;language-nix&quot; data-lang=&quot;nix&quot;&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;rustPlatform&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;buildRustPackage &lt;&#x2F;span&gt;&lt;span&gt;rec {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;# ...
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;LIBCLANG_PATH &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;llvmPackages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;libclang&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;lib&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;}&#x2F;lib&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;# ...
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next thing we needed to do was pass in some c flags to bindgen. I’m not too sure what these do, but they’re what was used by Firefox and they seem to work:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;configurePhase = &amp;#39;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  BINDGEN_CFLAGS=&amp;quot;$(&amp;lt; ${stdenv.cc}&#x2F;nix-support&#x2F;libc-crt1-cflags) \
&lt;&#x2F;span&gt;&lt;span&gt;    $(&amp;lt; ${stdenv.cc}&#x2F;nix-support&#x2F;libc-cflags) \
&lt;&#x2F;span&gt;&lt;span&gt;    $(&amp;lt; ${stdenv.cc}&#x2F;nix-support&#x2F;cc-cflags) \
&lt;&#x2F;span&gt;&lt;span&gt;    $(&amp;lt; ${stdenv.cc}&#x2F;nix-support&#x2F;libcxx-cxxflags) \
&lt;&#x2F;span&gt;&lt;span&gt;    ${lib.optionalString stdenv.cc.isClang &amp;quot;-idirafter ${stdenv.cc.cc.lib}&#x2F;lib&#x2F;clang&#x2F;${lib.getVersion stdenv.cc.cc}&#x2F;include&amp;quot;} \
&lt;&#x2F;span&gt;&lt;span&gt;    ${lib.optionalString stdenv.cc.isGNU &amp;quot;-isystem ${lib.getDev stdenv.cc.cc}&#x2F;include&#x2F;c++&#x2F;${lib.getVersion stdenv.cc.cc} -isystem ${stdenv.cc.cc}&#x2F;include&#x2F;c++&#x2F;${lib.getVersion stdenv.cc.cc}&#x2F;${stdenv.hostPlatform.config}&amp;quot;} \
&lt;&#x2F;span&gt;&lt;span&gt;    $NIX_CFLAGS_COMPILE&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  export OUT=${placeholder &amp;quot;out&amp;quot;}
&lt;&#x2F;span&gt;&lt;span&gt;  echo $OUT
&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&amp;#39;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which is a lot, but it isn’t a lot a lot. But wait! how does Rust know where libcec is during compilation? obviously hardcoding something in the &lt;code&gt;store&lt;&#x2F;code&gt; is a bad idea!&lt;&#x2F;p&gt;
&lt;p&gt;During the patch phase (which happens before the configuration phase seen above), we can perform a substitution. In &lt;code&gt;build.rs&lt;&#x2F;code&gt;, we can switch out the builder to be:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; bindings = bindgen::Builder::default()
&lt;&#x2F;span&gt;&lt;span&gt;    .header(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;LIBCEC_HEADERS&#x2F;include&#x2F;libcec&#x2F;cecc.h&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then substitute out LIBCEC_HEADERS with the path to the actual headers:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;nix&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-nix &quot;&gt;&lt;code class=&quot;language-nix&quot; data-lang=&quot;nix&quot;&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;patchPhase &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;#39;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;  substituteInPlace build.rs --replace &amp;quot;LIBCEC_HEADERS&amp;quot; &amp;quot;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;libcec&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;}&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This seems like a bit of a hack, but hey, at least it works! Last but not least, we can set our build inputs, and…&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;nativeBuildInputs = [
&lt;&#x2F;span&gt;&lt;span&gt;  llvmPackages.clang
&lt;&#x2F;span&gt;&lt;span&gt;  tree
&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;buildInputs = [
&lt;&#x2F;span&gt;&lt;span&gt;  libcec glibc
&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Tada! Everything works as intended! There’s a little cleanup that needs to be done to extract the resulting Rust binary, but I’ll spare you the details.&lt;&#x2F;p&gt;
&lt;p&gt;NixOS is pretty cool, but it seems to be targeted towards people who like to roll their own solutions and stick to open source software. This is great! If you do decide to go all in, though, be prepared!&lt;&#x2F;p&gt;
&lt;p&gt;Thanks again to everyone who helped me resolve this issue! Nix has shown me how many assumptions are present when building modern software, and it’s surprising how good of a job they’ve done categorizing different issues and dependencies and making reproducible builds as easy as &lt;code&gt;nix build&lt;&#x2F;code&gt;. ’Til next time!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Lenguas del mundo hispánico</title>
        <published>2021-06-09T00:00:00+00:00</published>
        <updated>2021-06-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/gallery/lenguas/"/>
        <id>https://slightknack.dev/gallery/lenguas/</id>
        
        <content type="html" xml:base="https://slightknack.dev/gallery/lenguas/">
&lt;style&gt;
#floating-nav-img {
  vertical-align: middle;
  display: inline-block;
  width: 30pt;
  height: 30pt;
  border-radius: 40pt;
  margin: 0;
  padding: 0;
}

#floating-nav {
  background: var(--fill-bg);
  border-radius: 40pt;
  position: fixed;
  z-index: 999;
  top: 20pt;
  right: 20pt;
  border: 2px solid var(--fill-bg);
}

#floating-nav &gt; .tag {
  display: none
}

#floating-nav:hover &gt; .tag {
  display: initial
}

&lt;&#x2F;style&gt;

&lt;div id=&quot;floating-nav&quot;&gt;
    &lt;span class=&quot;tag&quot; style=&quot;padding-left: 20pt;&quot;&gt;&lt;a href=&quot;&#x2F;about&quot;&gt;Isaac Clayton&lt;&#x2F;a&gt; · &lt;a href=&quot;&#x2F;gallery&quot;&gt;Gallery&lt;&#x2F;a&gt; · &lt;&#x2F;span&gt;
    &lt;a href=&quot;&#x2F;&quot; class=&quot;plain&quot;&gt;
        &lt;img id=&quot;floating-nav-img&quot; src=&quot;&#x2F;icon.png&quot;&gt;&lt;&#x2F;img&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;iframe style=&quot;height: 100vh; width: 100%; margin-bottom: -2vh;&quot; src=&quot;https:&#x2F;&#x2F;www.figma.com&#x2F;embed?embed_host=share&amp;url=https%3A%2F%2Fwww.figma.com%2Fproto%2FE8GflU8DuOPVBDVsNtpfG6%2FInfograf%C3%ADa-Sobre-Idiomas%3Fnode-id%3D1%253A4%26scaling%3Dscale-down-width&quot; allowfullscreen=&quot;&quot;&gt;&lt;&#x2F;iframe&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Shader Portal</title>
        <published>2021-06-09T00:00:00+00:00</published>
        <updated>2021-06-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/gallery/portal/"/>
        <id>https://slightknack.dev/gallery/portal/</id>
        
        <content type="html" xml:base="https://slightknack.dev/gallery/portal/">
&lt;style&gt;
#floating-nav-img {
  vertical-align: middle;
  display: inline-block;
  width: 30pt;
  height: 30pt;
  border-radius: 40pt;
  margin: 0;
  padding: 0;
}

#floating-nav {
  background: var(--fill-bg);
  border-radius: 40pt;
  position: fixed;
  z-index: 999;
  top: 20pt;
  right: 20pt;
  border: 2px solid var(--fill-bg);
}

#floating-nav &gt; .tag {
  display: none
}

#floating-nav:hover &gt; .tag {
  display: initial
}

&lt;&#x2F;style&gt;

&lt;div id=&quot;floating-nav&quot;&gt;
    &lt;span class=&quot;tag&quot; style=&quot;padding-left: 20pt;&quot;&gt;&lt;a href=&quot;&#x2F;about&quot;&gt;Isaac Clayton&lt;&#x2F;a&gt; · &lt;a href=&quot;&#x2F;gallery&quot;&gt;Gallery&lt;&#x2F;a&gt; · &lt;&#x2F;span&gt;
    &lt;a href=&quot;&#x2F;&quot; class=&quot;plain&quot;&gt;
        &lt;img id=&quot;floating-nav-img&quot; src=&quot;&#x2F;icon.png&quot;&gt;&lt;&#x2F;img&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Macros, Types, and Next Steps</title>
        <published>2021-06-01T00:00:00+00:00</published>
        <updated>2021-06-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/passerine/next-steps/"/>
        <id>https://slightknack.dev/passerine/next-steps/</id>
        
        <summary type="html">&lt;p&gt;Passerine is at an interesting point: we’ve established a few language features, and built this easily extensible functional core on which to base the rest of the language. We currently have two implementations of the language, one written in Rust, the other in D, and it’s imperative we set the course of the language before divergence occurs.&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Mirror: Hindley Milner Type Inference</title>
        <published>2021-05-01T00:00:00+00:00</published>
        <updated>2021-05-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/passerine/type-inference/"/>
        <id>https://slightknack.dev/passerine/type-inference/</id>
        
        <content type="html" xml:base="https://slightknack.dev/passerine/type-inference/">&lt;blockquote&gt;
&lt;p&gt;This is a frozen mirror of &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.cs.cornell.edu&#x2F;courses&#x2F;cs3110&#x2F;2016fa&#x2F;l&#x2F;17-inference&#x2F;notes.html&quot;&gt;notes from Cornell’s CS3110&lt;&#x2F;a&gt;. Full disclosure, I did not write this.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;topics&quot;&gt;Topics&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Type inference and reconstruction&lt;&#x2F;li&gt;
&lt;li&gt;Constraint collection&lt;&#x2F;li&gt;
&lt;li&gt;Constraint solving (unification)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;intro&quot;&gt;Intro&lt;&#x2F;h2&gt;
&lt;p&gt;Java and OCaml are &lt;em&gt;statically typed&lt;&#x2F;em&gt; languages, meaning every binding has
a type that is determined at &lt;em&gt;compile time&lt;&#x2F;em&gt;—that is, before any part of
the program is executed. The type-checker is a compile-time procedure
that either accepts or rejects a program. By contrast, JavaScript and
Ruby are dynamically-typed languages; the type of a binding is not
determined ahead of time and computations like binding 42 to &lt;code&gt;x&lt;&#x2F;code&gt; and
then treating &lt;code&gt;x&lt;&#x2F;code&gt; as a string result in run-time errors.&lt;&#x2F;p&gt;
&lt;p&gt;Unlike Java, OCaml is &lt;em&gt;implicitly typed&lt;&#x2F;em&gt;, meaning programmers rarely need
to write down the types of bindings. This is often convenient,
especially with higher-order functions. (Although some people disagree
as to whether it makes code easier or harder to read). But implicit
typing in no way changes the fact that OCaml is statically typed. Rather,
the type-checker has to be more sophisticated because it must infer what
the &lt;em&gt;type annotations&lt;&#x2F;em&gt; “would have been” had the programmers written all
of them. In principle, type inference and type checking could be
separate procedures (the inferencer could figure out the types then the
checker could determine whether the program is well-typed), but in
practice they are often merged into a single procedure called
&lt;em&gt;type reconstruction&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ocaml-type-reconstruction&quot;&gt;OCaml type reconstruction&lt;&#x2F;h2&gt;
&lt;p&gt;OCaml was rather cleverly designed so that type reconstruction is a
straightforward algorithm. At a very high level, that algorithm works as
follows:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Determine the types of definitions in order, using the types of earlier
definitions to infer the types of later ones. (Which is one reason you
may not use a name before it is bound in an OCaml program.)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;For each &lt;code&gt;let&lt;&#x2F;code&gt; definition, analyze the definition to determine
&lt;em&gt;constraints&lt;&#x2F;em&gt; about its type. For example, if the inferencer sees
&lt;code&gt;x+1&lt;&#x2F;code&gt;, it concludes that &lt;code&gt;x&lt;&#x2F;code&gt; must have type &lt;code&gt;int&lt;&#x2F;code&gt;. It gathers
similar constraints for function applications, pattern matches, etc.
Think of these constraints as a system of equations like you might
have in algebra.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Use that system of equations to solve for the type of the name
begin defined.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The OCaml type reconstruction algorithm attempts to never reject a
program that could type-check, if the programmer had written down types.
It also attempts never to accept a program that cannot possibly type
check. Some more obscure parts of the language can sometimes make type
annotations either necessary or at least helpful (see RWO chapter 22,
“Type inference”, for examples).  But for most code you write, type
annotations really are completely optional.&lt;&#x2F;p&gt;
&lt;p&gt;Since it would be verbose to keep writing “the OCaml type reconstruction
algorithm,” we’ll call the algorithm HM. That name is used throughout
the programming languages literature, because the algorithm was
independently invented by Roger &lt;u&gt;H&lt;&#x2F;u&gt;indley and Robin &lt;u&gt;M&lt;&#x2F;u&gt;ilner.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;collecting-and-solving-constraints-examples&quot;&gt;Collecting and solving constraints: Examples&lt;&#x2F;h2&gt;
&lt;p&gt;To gather the constraints for a definition, HM does the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Assign a preliminary type to every subexpression in the definition.
For known operations and constants, such as &lt;code&gt;+&lt;&#x2F;code&gt; and &lt;code&gt;3&lt;&#x2F;code&gt;, use the
type that is already known for it. For anything else, use a new type
variable that hasn’t been used anywhere else.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Use the “shape” of the expressions to generate constraints. For
example, if an expression involves applying a function to an
argument, then generate a constraint requiring the type of the
argument to be the same as the function’s input type.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We’ll give some examples of this first, then we’ll give the algorithms
for doing it.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;example-1&quot;&gt;Example 1.&lt;&#x2F;h4&gt;
&lt;p&gt;Here’s an example utop interaction:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;# let g x = 5 + x;;
&lt;&#x2F;span&gt;&lt;span&gt;val g : int -&amp;gt; int = &amp;lt;fun&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;How did OCaml infer the type of &lt;code&gt;g&lt;&#x2F;code&gt; here?  Let’s work it out.&lt;&#x2F;p&gt;
&lt;p&gt;First, let’s rewrite &lt;code&gt;g&lt;&#x2F;code&gt; syntactically to make our work a little easier:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;let g = fun x -&amp;gt; ((+) 5) x
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We’ve made the anonymous function explicit, and we’ve made the
binary infix operator a prefix function application.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;1. Assign preliminary types.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For each subexpression of &lt;code&gt;fun x -&amp;gt; (+) 5 x&lt;&#x2F;code&gt;, including the entire
expression itself, we assign a preliminary type. We already know the
types of &lt;code&gt;(+)&lt;&#x2F;code&gt; and &lt;code&gt;5&lt;&#x2F;code&gt;, because those are baked into the language
itself, but for everything else we “play dumb” and just invent a new
type variable for it. For now we will use uppercase letters to represent
those type variables, rather than the OCaml syntax for type variables
(e.g., &lt;code&gt;&#x27;a&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Subexpression         Preliminary type
&lt;&#x2F;span&gt;&lt;span&gt;------------------    --------------------
&lt;&#x2F;span&gt;&lt;span&gt;fun x -&amp;gt; ((+) 5) x    R
&lt;&#x2F;span&gt;&lt;span&gt;    x                 U
&lt;&#x2F;span&gt;&lt;span&gt;         ((+) 5) x    S
&lt;&#x2F;span&gt;&lt;span&gt;         ((+) 5)      T
&lt;&#x2F;span&gt;&lt;span&gt;          (+)         int -&amp;gt; (int -&amp;gt; int)
&lt;&#x2F;span&gt;&lt;span&gt;              5       int
&lt;&#x2F;span&gt;&lt;span&gt;                 x    V
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;2. Collect constraints.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here are some observations we could make about the “shape” of subexpressions
and some relationships among them:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Since function argument &lt;code&gt;x&lt;&#x2F;code&gt; has type &lt;code&gt;U&lt;&#x2F;code&gt; and function body &lt;code&gt;((+) 5) x&lt;&#x2F;code&gt;
has type &lt;code&gt;S&lt;&#x2F;code&gt;, it must be the case that &lt;code&gt;R&lt;&#x2F;code&gt;, the type of the anonymous
function expression, satisfies the constraint &lt;code&gt;R = U -&amp;gt; S&lt;&#x2F;code&gt;.
That is, &lt;em&gt;the type of the anonymous function&lt;&#x2F;em&gt; is &lt;em&gt;the type of its argument&lt;&#x2F;em&gt;
arrow &lt;em&gt;the type of its body&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Since function &lt;code&gt;((+) 5)&lt;&#x2F;code&gt; has type &lt;code&gt;T&lt;&#x2F;code&gt; and function
application &lt;code&gt;((+) 5) x&lt;&#x2F;code&gt; has type &lt;code&gt;S&lt;&#x2F;code&gt;, and since the argument &lt;code&gt;x&lt;&#x2F;code&gt; has
type &lt;code&gt;V&lt;&#x2F;code&gt;, it must be the case that &lt;code&gt;T = V -&amp;gt; S&lt;&#x2F;code&gt;.  That is,
&lt;em&gt;the type of the function being applied&lt;&#x2F;em&gt; is &lt;em&gt;the type of the argument it’s
being applied to&lt;&#x2F;em&gt; arrow &lt;em&gt;the type of the function application expression&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Since function &lt;code&gt;(+)&lt;&#x2F;code&gt; has type &lt;code&gt;int -&amp;gt; (int -&amp;gt; int)&lt;&#x2F;code&gt; and function
application &lt;code&gt;(+) 5&lt;&#x2F;code&gt; has type &lt;code&gt;T&lt;&#x2F;code&gt;, and since the argument &lt;code&gt;5&lt;&#x2F;code&gt;
has type &lt;code&gt;int&lt;&#x2F;code&gt;, it must be the case that &lt;code&gt;int -&amp;gt; (int-&amp;gt;int) = int -&amp;gt; T&lt;&#x2F;code&gt;.
Once again,
&lt;em&gt;the type of the function being applied&lt;&#x2F;em&gt; is &lt;em&gt;the type of the argument it’s
being applied to&lt;&#x2F;em&gt; arrow &lt;em&gt;the type of the function application expression&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Since &lt;code&gt;x&lt;&#x2F;code&gt; occurs with both type &lt;code&gt;U&lt;&#x2F;code&gt; and &lt;code&gt;V&lt;&#x2F;code&gt;, it must be the case that &lt;code&gt;U = V&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The set of constraints thus generated is:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;                  U = V
&lt;&#x2F;span&gt;&lt;span&gt;                  R = U -&amp;gt; S
&lt;&#x2F;span&gt;&lt;span&gt;                  T = V -&amp;gt; S
&lt;&#x2F;span&gt;&lt;span&gt;int -&amp;gt; (int -&amp;gt; int) = int -&amp;gt; T
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;3. Solve constraints.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can solve that system of equations easily. Starting from the last
constraint, we know &lt;code&gt;T&lt;&#x2F;code&gt; must be &lt;code&gt;int -&amp;gt; int&lt;&#x2F;code&gt;. Substituting that into the
second constraint, we get that &lt;code&gt;int -&amp;gt; int&lt;&#x2F;code&gt; must equal &lt;code&gt;V -&amp;gt; S&lt;&#x2F;code&gt;, hence
&lt;code&gt;V = S = int&lt;&#x2F;code&gt;. Since &lt;code&gt;U=V&lt;&#x2F;code&gt;, &lt;code&gt;U&lt;&#x2F;code&gt; must also be &lt;code&gt;int&lt;&#x2F;code&gt;. Substituting for &lt;code&gt;S&lt;&#x2F;code&gt;
and &lt;code&gt;U&lt;&#x2F;code&gt; in the first constraint, we get that &lt;code&gt;R = int -&amp;gt; int&lt;&#x2F;code&gt;. So the
inferred type of &lt;code&gt;g&lt;&#x2F;code&gt; is &lt;code&gt;int -&amp;gt; int&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;example-2&quot;&gt;Example 2.&lt;&#x2F;h4&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;# let apply f x = f x;;
&lt;&#x2F;span&gt;&lt;span&gt;val apply : (&amp;#39;a -&amp;gt; &amp;#39;b) -&amp;gt; &amp;#39;a -&amp;gt; &amp;#39;b = &amp;lt;fun&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Again we rewrite:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;let apply = fun f -&amp;gt; (fun x -&amp;gt; f x)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;1. Assign preliminary types.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Subexpression              Preliminary type
&lt;&#x2F;span&gt;&lt;span&gt;-----------------------    ------------------
&lt;&#x2F;span&gt;&lt;span&gt;fun f -&amp;gt; (fun x -&amp;gt; f x)    R
&lt;&#x2F;span&gt;&lt;span&gt;    f                      S
&lt;&#x2F;span&gt;&lt;span&gt;         (fun x -&amp;gt; f x)    T
&lt;&#x2F;span&gt;&lt;span&gt;              x            U
&lt;&#x2F;span&gt;&lt;span&gt;                   f x     V
&lt;&#x2F;span&gt;&lt;span&gt;                   f       S
&lt;&#x2F;span&gt;&lt;span&gt;                     x     U
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;2. Collect constraints.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;R = S -&amp;gt; T&lt;&#x2F;code&gt;, because of the anonymous function expression.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;T = U -&amp;gt; V&lt;&#x2F;code&gt;, because of the nested anonymous function expression.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;S = U -&amp;gt; V&lt;&#x2F;code&gt;, because of the function application.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;3. Solve constraints.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Using the third constraint, and substituting for &lt;code&gt;S&lt;&#x2F;code&gt; in the first
constraint, we have that &lt;code&gt;R = (U -&amp;gt; V) -&amp;gt; T&lt;&#x2F;code&gt;.  Using the second
constraint, and substituting for &lt;code&gt;T&lt;&#x2F;code&gt; in the first constraint,
we have that &lt;code&gt;R = (U -&amp;gt; V) -&amp;gt; (U -&amp;gt; V)&lt;&#x2F;code&gt;.  There are no further
substitutions that can be made, so we’re done solving the constraints.
If we now replace the preliminary type variables with actual OCaml
type variables, specifically &lt;code&gt;U&lt;&#x2F;code&gt; with &lt;code&gt;&#x27;a&lt;&#x2F;code&gt; and &lt;code&gt;V&lt;&#x2F;code&gt; with &lt;code&gt;&#x27;b&lt;&#x2F;code&gt;, we get that
the type of &lt;code&gt;apply&lt;&#x2F;code&gt; is &lt;code&gt;(&#x27;a -&amp;gt; &#x27;b) -&amp;gt; (&#x27;a -&amp;gt; &#x27;b)&lt;&#x2F;code&gt;, which is the same as
&lt;code&gt;(&#x27;a -&amp;gt; &#x27;b) -&amp;gt; &#x27;a -&amp;gt; &#x27;b&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;example-3&quot;&gt;Example 3.&lt;&#x2F;h4&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;# apply g 3;;
&lt;&#x2F;span&gt;&lt;span&gt;- : int = 8
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We rewrite that as &lt;code&gt;(apply g) 3&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;1. Assign preliminary types.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In this running example, the inference for &lt;code&gt;g&lt;&#x2F;code&gt; and &lt;code&gt;apply&lt;&#x2F;code&gt; has already
been done, so we can fill in their types as known, much like the type
of &lt;code&gt;+&lt;&#x2F;code&gt; is already known.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;Subexpression     Preliminary type
&lt;&#x2F;span&gt;&lt;span&gt;-------------     ------------------------------------------
&lt;&#x2F;span&gt;&lt;span&gt;(apply g) 3       R
&lt;&#x2F;span&gt;&lt;span&gt;(apply g)         S  
&lt;&#x2F;span&gt;&lt;span&gt; apply            (U -&amp;gt; V) -&amp;gt; (U -&amp;gt; V)
&lt;&#x2F;span&gt;&lt;span&gt;       g          int -&amp;gt; int
&lt;&#x2F;span&gt;&lt;span&gt;          3       int
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;2. Collect constraints.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;S = int -&amp;gt; R&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;`(U -&amp;gt; V) -&amp;gt; (U -&amp;gt; V) = (int -&amp;gt; int) -&amp;gt; S&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;3. Solve constraints.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Breaking down the last constraint, we have that &lt;code&gt;U = V = int&lt;&#x2F;code&gt;, and
that &lt;code&gt;S = U -&amp;gt; V&lt;&#x2F;code&gt;, hence &lt;code&gt;S = int -&amp;gt; int&lt;&#x2F;code&gt;.  Substituting that into
the first constraint, we have that &lt;code&gt;int -&amp;gt; int = int -&amp;gt; R&lt;&#x2F;code&gt;.  Therefore
&lt;code&gt;R = int&lt;&#x2F;code&gt;, so the type of &lt;code&gt;apply g 3&lt;&#x2F;code&gt; is &lt;code&gt;int&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;example-4&quot;&gt;Example 4.&lt;&#x2F;h4&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;# apply not false;;
&lt;&#x2F;span&gt;&lt;span&gt;- : bool = true
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By essentially the same reasoning as in example 3, HM can infer that the
type of this expression is &lt;code&gt;bool&lt;&#x2F;code&gt;. This illustrates the polymorphism of
&lt;code&gt;apply&lt;&#x2F;code&gt;: because the type &lt;code&gt;(U -&amp;gt; V) -&amp;gt; (U -&amp;gt; V)&lt;&#x2F;code&gt; of
&lt;code&gt;apply&lt;&#x2F;code&gt; contains type variables, the function can be applied to any
arguments, so long as those arguments’ types can be consistently
substituted for the type variables.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;collecting-constraints-algorithm&quot;&gt;Collecting constraints: Algorithm&lt;&#x2F;h2&gt;
&lt;p&gt;We now present an algorithm that generates constraints. This algorithm
is a precise description of how constraint gathering works in the
examples we discussed above. The algorithm is not exactly what HM does,
because HM actually performs type checking at the same time as type
inference. However, the resulting types are the same, and separating
inference from checking hopefully will give you a clearer idea of how
inference itself works.&lt;&#x2F;p&gt;
&lt;p&gt;The algorithm takes as input an expression &lt;code&gt;e&lt;&#x2F;code&gt;. We’ll
assume that every function &lt;code&gt;fun x -&amp;gt; e&#x27;&lt;&#x2F;code&gt; in that expression has an
argument with a different name. (If not, our algorithm could make a
pre-pass to rename variables. This is feasible because of lexical scope.)
The output of the algorithm is a set of constraints.&lt;&#x2F;p&gt;
&lt;p&gt;The first thing the algorithm does is to assign unique preliminary
type variables, e.g. &lt;code&gt;R&lt;&#x2F;code&gt; or &lt;code&gt;S&lt;&#x2F;code&gt;,&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;one to each &lt;em&gt;defining&lt;&#x2F;em&gt; occurrence of a variable, which could be as
a function argument or a let binding, and&lt;&#x2F;li&gt;
&lt;li&gt;one to each occurrence of each subexpression of &lt;code&gt;e&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Call the type variable assigned to &lt;code&gt;x&lt;&#x2F;code&gt; in the former clause
&lt;code&gt;D(x)&lt;&#x2F;code&gt;, and call the type variable assigned to occurrence of a
subexpression &lt;code&gt;e&#x27;&lt;&#x2F;code&gt; in the latter clause &lt;code&gt;U(e&#x27;)&lt;&#x2F;code&gt;.  The names of these
are mnemonics:  &lt;code&gt;U&lt;&#x2F;code&gt; stands for the &lt;u&gt;u&lt;&#x2F;u&gt;se of an expression,
and &lt;code&gt;D&lt;&#x2F;code&gt; stands for the &lt;u&gt;d&lt;&#x2F;u&gt;efinition of a variable name.&lt;&#x2F;p&gt;
&lt;p&gt;Next, the algorithm generates the following constraints:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;For integer constants &lt;code&gt;n&lt;&#x2F;code&gt;:  &lt;code&gt;U(n) = int&lt;&#x2F;code&gt;.  This constraints follows
from the type checking rule for integers, which says that every
integer constant has type &lt;code&gt;int&lt;&#x2F;code&gt;.  Constraints for other types of
constants are generated in a similar way.&lt;&#x2F;li&gt;
&lt;li&gt;For variables &lt;code&gt;x&lt;&#x2F;code&gt;:  &lt;code&gt;D(x) = U(x)&lt;&#x2F;code&gt;.  This constraint follows from the type
checking rule for variables, which says the type of a variable use (in this case, &lt;code&gt;U(x)&lt;&#x2F;code&gt;)
must be the same as the type at which that variable was defined (here, &lt;code&gt;D(x)&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;For function application &lt;code&gt;e1 e2&lt;&#x2F;code&gt;: &lt;code&gt;U(e1) = U(e2) -&amp;gt; U(e1 e2)&lt;&#x2F;code&gt;,
as well as any constraints resulting from &lt;code&gt;e1&lt;&#x2F;code&gt; and &lt;code&gt;e2&lt;&#x2F;code&gt;.  This constraint follows
from the type checking rule for function application.&lt;&#x2F;li&gt;
&lt;li&gt;For anonymous functions &lt;code&gt;fun x -&amp;gt; e&lt;&#x2F;code&gt;: &lt;code&gt;U(fun x -&amp;gt; e) = D(x) -&amp;gt; U(e)&lt;&#x2F;code&gt;,
as well as any constraints resulting from &lt;code&gt;e&lt;&#x2F;code&gt;.  This constraint follows from the
type checking rule for anonymous functions.&lt;&#x2F;li&gt;
&lt;li&gt;For let expressions &lt;code&gt;let x=e1 in e2&lt;&#x2F;code&gt;: &lt;code&gt;D(x)=U(e1)&lt;&#x2F;code&gt;, &lt;code&gt;U(let x=e1 in e2) = U(e2)&lt;&#x2F;code&gt;,
as well as any constraints resulting from &lt;code&gt;e1&lt;&#x2F;code&gt; and &lt;code&gt;e2&lt;&#x2F;code&gt;.  This constraint follows
from the type checking rule for let expressions.&lt;&#x2F;li&gt;
&lt;li&gt;Other expression forms:  similar kinds of constraints likewise derived from the
type checking rule for the expression form.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The result is a set of constraints, which is the output of the
algorithm. It’s not too hard to implement this algorithm as a recursive
function over a tree representing the syntax of &lt;code&gt;e&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Example.&lt;&#x2F;strong&gt;
Given expression &lt;code&gt;fun x -&amp;gt; (fun y -&amp;gt; x)&lt;&#x2F;code&gt;, a type variable &lt;code&gt;R&lt;&#x2F;code&gt; is
associated with argument &lt;code&gt;x&lt;&#x2F;code&gt;, and &lt;code&gt;S&lt;&#x2F;code&gt; with argument &lt;code&gt;y&lt;&#x2F;code&gt;.  For
subexpressions, &lt;code&gt;T&lt;&#x2F;code&gt; is associated with the occurrence of &lt;code&gt;fun x -&amp;gt; (fun y -&amp;gt; x)&lt;&#x2F;code&gt;, and &lt;code&gt;X&lt;&#x2F;code&gt; with the occurrence of &lt;code&gt;(fun y -&amp;gt; x)&lt;&#x2F;code&gt;, and &lt;code&gt;Y&lt;&#x2F;code&gt; with
the occurrence of &lt;code&gt;x&lt;&#x2F;code&gt;. (Note that the names we’ve chosen for the type
variables are completely arbitrary.) The constraints generated are &lt;code&gt;T = R -&amp;gt; X&lt;&#x2F;code&gt;, and &lt;code&gt;X = S -&amp;gt; Y&lt;&#x2F;code&gt;, and &lt;code&gt;Y = R&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;solving-constraints-algorithm&quot;&gt;Solving constraints: Algorithm&lt;&#x2F;h2&gt;
&lt;p&gt;What does it mean to solve a set of constraints? To answer this
question, we define &lt;em&gt;type substitutions&lt;&#x2F;em&gt;. A type substitution is a map
from a type variable to a type. We’ll write &lt;code&gt;{t&#x2F;X}&lt;&#x2F;code&gt; for the
substitution that maps type variable &lt;code&gt;X&lt;&#x2F;code&gt; to type &lt;code&gt;t&lt;&#x2F;code&gt;. The way a
substitution &lt;code&gt;S&lt;&#x2F;code&gt; operates on a type can be defined recursively:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;S(X)        = if S = {t&#x2F;X} then t else X
&lt;&#x2F;span&gt;&lt;span&gt;S(t1 -&amp;gt; t2) = S(t1) -&amp;gt; S(t2)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A substitution &lt;code&gt;S&lt;&#x2F;code&gt; can be applied to a constraint &lt;code&gt;t = t&#x27;&lt;&#x2F;code&gt;; the result
&lt;code&gt;S(t = t&#x27;)&lt;&#x2F;code&gt; is defined to be &lt;code&gt;S(t) = S(t&#x27;)&lt;&#x2F;code&gt;. And a substitution can be
applied to a set &lt;code&gt;C&lt;&#x2F;code&gt; of constraints; the result &lt;code&gt;S(C)&lt;&#x2F;code&gt; is the result of
applying &lt;code&gt;S&lt;&#x2F;code&gt; to each of the individual constraints in &lt;code&gt;C&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Given two substitutions &lt;code&gt;S&lt;&#x2F;code&gt; and &lt;code&gt;S&#x27;&lt;&#x2F;code&gt;, we write &lt;code&gt;S;S&#x27;&lt;&#x2F;code&gt; for their
composition: &lt;code&gt;(S;S&#x27;)(t) = S&#x27;(S(t))&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A substitution &lt;em&gt;unifies&lt;&#x2F;em&gt; a constraint &lt;code&gt;t_1 = t_2&lt;&#x2F;code&gt; if &lt;code&gt;S(t_1) = S(t_2)&lt;&#x2F;code&gt;.
A substitution &lt;code&gt;S&lt;&#x2F;code&gt; unifies a set &lt;code&gt;C&lt;&#x2F;code&gt; of constraints if &lt;code&gt;S&lt;&#x2F;code&gt; unifies every
constraint in &lt;code&gt;C&lt;&#x2F;code&gt;. For example, substitution
&lt;code&gt;S = {int-&amp;gt;int&#x2F;Y};{int&#x2F;X}&lt;&#x2F;code&gt; unifies constraint &lt;code&gt;X -&amp;gt; (X -&amp;gt; int) = int -&amp;gt; Y&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To solve a set of constraints &lt;code&gt;C&lt;&#x2F;code&gt;, we need to find a substitution that
unifies &lt;code&gt;C&lt;&#x2F;code&gt;. If there are no substitutions that unify &lt;code&gt;C&lt;&#x2F;code&gt;, where &lt;code&gt;C&lt;&#x2F;code&gt;
is the constraints generated from expression &lt;code&gt;e&lt;&#x2F;code&gt;, then &lt;code&gt;e&lt;&#x2F;code&gt; is not
typeable.&lt;&#x2F;p&gt;
&lt;p&gt;To find a substitution that unifies &lt;code&gt;C&lt;&#x2F;code&gt;, we use an algorithm
appropriately called the &lt;em&gt;unification&lt;&#x2F;em&gt; algorithm. It is defined as
follows:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;if &lt;code&gt;C&lt;&#x2F;code&gt; is the empty set, then &lt;code&gt;unify(C)&lt;&#x2F;code&gt; is the empty substitution.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;if &lt;code&gt;C&lt;&#x2F;code&gt; is the union of a constraint &lt;code&gt;t = t&#x27;&lt;&#x2F;code&gt; with other constraints &lt;code&gt;C&#x27;&lt;&#x2F;code&gt;, then
&lt;code&gt;unify(C)&lt;&#x2F;code&gt; is defined as follows, based on that constraint:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;if &lt;code&gt;t&lt;&#x2F;code&gt; and &lt;code&gt;t&#x27;&lt;&#x2F;code&gt; are both the same type variable, e.g. &lt;code&gt;X&lt;&#x2F;code&gt;,
then return &lt;code&gt;unify(C&#x27;)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;if &lt;code&gt;t = X&lt;&#x2F;code&gt; for some type variable &lt;code&gt;X&lt;&#x2F;code&gt;, and &lt;code&gt;X&lt;&#x2F;code&gt; does not occur in &lt;code&gt;t&#x27;&lt;&#x2F;code&gt;,
then let &lt;code&gt;S = {t&#x27;&#x2F;X}&lt;&#x2F;code&gt;, and return &lt;code&gt;unify(S(C&#x27;));S&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;if &lt;code&gt;t&#x27; = X&lt;&#x2F;code&gt; for some type variable &lt;code&gt;X&lt;&#x2F;code&gt;, and &lt;code&gt;X&lt;&#x2F;code&gt; does not occur in &lt;code&gt;t&lt;&#x2F;code&gt;,
then let &lt;code&gt;S = {t&#x2F;X}&lt;&#x2F;code&gt;, and return &lt;code&gt;unify(S(C&#x27;));S&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;if &lt;code&gt;t = t0 -&amp;gt; t1&lt;&#x2F;code&gt; and &lt;code&gt;t&#x27; = t&#x27;0 -&amp;gt; t&#x27;1&lt;&#x2F;code&gt;,
then let &lt;code&gt;C&#x27;&#x27;&lt;&#x2F;code&gt; be the union of &lt;code&gt;C&#x27;&lt;&#x2F;code&gt; with the constraints
&lt;code&gt;t0 = t&#x27;0&lt;&#x2F;code&gt; and &lt;code&gt;t1 = t&#x27;1&lt;&#x2F;code&gt;, and return &lt;code&gt;unify(C&#x27;&#x27;)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;if &lt;code&gt;t = t0 * t1&lt;&#x2F;code&gt; and &lt;code&gt;t&#x27; = t&#x27;0 * t&#x27;1&lt;&#x2F;code&gt;,
then let &lt;code&gt;C&#x27;&#x27;&lt;&#x2F;code&gt; be the union of &lt;code&gt;C&#x27;&lt;&#x2F;code&gt; with the constraints
&lt;code&gt;t0 = t&#x27;0&lt;&#x2F;code&gt; and &lt;code&gt;t1 = t&#x27;1&lt;&#x2F;code&gt;, and return &lt;code&gt;unify(C&#x27;&#x27;)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;if &lt;code&gt;t = (t0, ..., tn) tc&lt;&#x2F;code&gt; and &lt;code&gt;t&#x27; = (t&#x27;0, ..., t&#x27;n) tc&lt;&#x2F;code&gt; for some
type constructor &lt;code&gt;tc&lt;&#x2F;code&gt;,
then let &lt;code&gt;C&#x27;&#x27;&lt;&#x2F;code&gt; be the union of &lt;code&gt;C&#x27;&lt;&#x2F;code&gt; with the constraints
&lt;code&gt;ti = t&#x27;i&lt;&#x2F;code&gt;, and return &lt;code&gt;unify(C&#x27;&#x27;)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;otherwise, fail. There is no possible unifier.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In the second and third subcases, the check that &lt;code&gt;X&lt;&#x2F;code&gt; should
not occur in &lt;code&gt;t&lt;&#x2F;code&gt; ensures that the algorithm doesn’t produce a cyclic
substitution—for example, &lt;code&gt;{(X -&amp;gt; X) &#x2F; X}&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It’s possible to prove that the unification algorithm always terminates,
and that it produces a result if and only a unifier actually exists—that
is, if and only if the set of constraints has a solution. Moreover, the
solution the algorithm produces is the &lt;em&gt;most general unifier&lt;&#x2F;em&gt;, in the
sense that if &lt;code&gt;S = unify(C)&lt;&#x2F;code&gt; and &lt;code&gt;S&#x27;&lt;&#x2F;code&gt; unifies &lt;code&gt;C&lt;&#x2F;code&gt;, then there
must exist some &lt;code&gt;S&#x27;&#x27;&lt;&#x2F;code&gt; such that &lt;code&gt;S&#x27; = S;S&#x27;&#x27;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If &lt;code&gt;R&lt;&#x2F;code&gt; is the type variable assigned to represent the type of the entire
expression &lt;code&gt;e&lt;&#x2F;code&gt;, and if &lt;code&gt;S&lt;&#x2F;code&gt; is the substitution produced by the
algorithm, then &lt;code&gt;S(R)&lt;&#x2F;code&gt; is the type inferred for &lt;code&gt;e&lt;&#x2F;code&gt; by HM type
inference. Call that type &lt;code&gt;t&lt;&#x2F;code&gt;. It’s possible to prove &lt;code&gt;t&lt;&#x2F;code&gt; is the
&lt;em&gt;principal&lt;&#x2F;em&gt; type for the expression, meaning that if &lt;code&gt;e&lt;&#x2F;code&gt; also has type
&lt;code&gt;t&#x27;&lt;&#x2F;code&gt; for any other &lt;code&gt;t&#x27;&lt;&#x2F;code&gt;, then there exists a substitution &lt;code&gt;S&lt;&#x2F;code&gt; such that
&lt;code&gt;t&#x27; = S(t)&lt;&#x2F;code&gt;. So HM actually infers the most lenient type that is possible
for any expression.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;let-expressions&quot;&gt;Let expressions&lt;&#x2F;h2&gt;
&lt;p&gt;Consider the following code:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;let double f z = f (f z) in
&lt;&#x2F;span&gt;&lt;span&gt;(double (fun x -&amp;gt; x+1) 1, double (fun x -&amp;gt; not x) false)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The inferred type for &lt;code&gt;f&lt;&#x2F;code&gt; in &lt;code&gt;double&lt;&#x2F;code&gt; would be &lt;code&gt;X -&amp;gt; X&lt;&#x2F;code&gt;. In the
algorithm we’ve described so far, the use of &lt;code&gt;double&lt;&#x2F;code&gt; in the first
component of the pair would produce the constraint &lt;code&gt;X = int&lt;&#x2F;code&gt;, and the
use of &lt;code&gt;double&lt;&#x2F;code&gt; in the definition of &lt;code&gt;b&lt;&#x2F;code&gt; would produce the constraint &lt;code&gt;X = bool&lt;&#x2F;code&gt;. Those constraints would be contradictory, causing unification
to fail!&lt;&#x2F;p&gt;
&lt;p&gt;There is a very nice solution to this called &lt;em&gt;let-polymorphism&lt;&#x2F;em&gt;, which
is what OCaml actually uses. Let-polymorphism enables a polymorphic
function bound by a &lt;code&gt;let&lt;&#x2F;code&gt; expression behave as though it has multiple
types. The essential idea is to allow each usage of a polymorphic
function to have its own instantiation of the type variables, so that
contradictions like the one above can’t happen.&lt;&#x2F;p&gt;
&lt;p&gt;We won’t cover let-polymorphism here, but you can learn more about it
in the reading given below.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;efficiency-of-hm&quot;&gt;Efficiency of HM&lt;&#x2F;h2&gt;
&lt;p&gt;HM is usually a very efficient algorithm—you’ve probably never had to
wait for the REPL to print the inferred types of your programs. In
practice, it runs in approximately linear time. But in theory, there are
some very strange programs that can cause its running-time to blow up.
(Technically, it’s DEXPTIME-complete.) For fun, try typing the following
code in utop:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#151515;color:#e8e8d3;&quot;&gt;&lt;code&gt;&lt;span&gt;let b = true;;
&lt;&#x2F;span&gt;&lt;span&gt;let f0 = fun x -&amp;gt; x+1;;
&lt;&#x2F;span&gt;&lt;span&gt;let f = fun x -&amp;gt; if b then f0 else fun y -&amp;gt; x y;;
&lt;&#x2F;span&gt;&lt;span&gt;let f = fun x -&amp;gt; if b then f else fun y -&amp;gt; x y;;
&lt;&#x2F;span&gt;&lt;span&gt;(* keep repeating that last line *)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You’ll see the types get longer and longer, and eventually type inference
will cause a notable delay.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-history-of-hm&quot;&gt;The history of HM&lt;&#x2F;h2&gt;
&lt;p&gt;HM has been rediscovered many times by many people. Curry used it
informally in the 1950’s (perhaps even the 1930’s). He wrote it up
formally in 1967 (published 1969). Hindley discovered it independently
in 1969; Morris in 1968; and Milner in 1978. In the realm of logic,
similar ideas go back perhaps as far as Tarski in the 1920’s. Commenting
on this history, Hindley wrote,&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;There must be a moral to this story of continual re-discovery;
perhaps someone along the line should have learned to read. Or someone
else learn to write.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;Hindley–Milner type inference is one of the core algorithms that
makes the OCaml language, and many other functional languages, possible.
It is fundamentally based on traversing the source code to collect
a system of equations, then solving that system to determine the types.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;terms-and-concepts&quot;&gt;Terms and concepts&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;constraint&lt;&#x2F;li&gt;
&lt;li&gt;Hindley–Milner (HM) type inference algorithm&lt;&#x2F;li&gt;
&lt;li&gt;implicit typing&lt;&#x2F;li&gt;
&lt;li&gt;let polymorphism&lt;&#x2F;li&gt;
&lt;li&gt;preliminary type variable&lt;&#x2F;li&gt;
&lt;li&gt;static typing&lt;&#x2F;li&gt;
&lt;li&gt;substitution&lt;&#x2F;li&gt;
&lt;li&gt;type annotation&lt;&#x2F;li&gt;
&lt;li&gt;type inference&lt;&#x2F;li&gt;
&lt;li&gt;type reconstruction&lt;&#x2F;li&gt;
&lt;li&gt;unification&lt;&#x2F;li&gt;
&lt;li&gt;unifier&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;newcatalog.library.cornell.edu&#x2F;catalog&#x2F;8324012&quot;&gt;&lt;em&gt;Types and Programming Languages&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;, chapter 22, by Benjamin C. Pierce.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A Small Test Post for this New Blog</title>
        <published>2021-04-30T00:00:00+00:00</published>
        <updated>2021-04-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/first/"/>
        <id>https://slightknack.dev/blog/first/</id>
        
        <content type="html" xml:base="https://slightknack.dev/blog/first/">&lt;h1 id=&quot;hello&quot;&gt;Hello!&lt;&#x2F;h1&gt;
&lt;p&gt;This is my first blog post.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; this is a function
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Hello!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I’m debating whether to keep this up… oh well ;P&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Primary Eye</title>
        <published>2021-03-12T00:00:00+00:00</published>
        <updated>2021-03-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/gallery/eye/"/>
        <id>https://slightknack.dev/gallery/eye/</id>
        
        <content type="html" xml:base="https://slightknack.dev/gallery/eye/">&lt;p&gt;Red, Yellow, Blue. These are the oft-repeated primary colors, but they are only a half-truth.&lt;&#x2F;p&gt;
&lt;p&gt;There are really two sets of primary colors; which one to use depends on the type of light you’re working with. For additive light, like that emitted by screens, the primary colors are Red, Green, and Blue — RGB.&lt;&#x2F;p&gt;
&lt;p&gt;The physical medium, on the other hand, is subtractive. Adding colored pigment to paper removes a fraction of the light being reflected off it. The primary colors of subtractive light are Cyan, Magenta, and Yellow (and Black) — CMYK.&lt;&#x2F;p&gt;
&lt;p&gt;It’s funny, then, that artists stick to Red, Yellow, and Blue. Artfully selected primaries create nuanced mood; why do we leave a fraction of the spectrum unexplored?&lt;&#x2F;p&gt;
&lt;p&gt;In this piece, I used only the 4 subtractive primaries, as seen in the bottom-left corner. This was a fun exercise in pulling out the components of colors, carefully mixing opposites to create a perfect shade in the middle.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Vaporization and Modern Memory Management</title>
        <published>2021-02-24T00:00:00+00:00</published>
        <updated>2021-02-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/passerine/vaporization/"/>
        <id>https://slightknack.dev/passerine/vaporization/</id>
        
        <summary type="html">&lt;blockquote&gt;
&lt;h2 id=&quot;note&quot;&gt;Note&lt;&#x2F;h2&gt;
&lt;p&gt;This piece is a work in progress.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;An interesting design space in the field of programming language design is that of memory management. In short, programs produce data while running. This in of itself isn’t much of a problem: in fact, it’s a good thing! If your programming language doesn’t allow for the production of any useful data, you might want to take a closer look at it…&lt;&#x2F;p&gt;
&lt;p&gt;As time goes on, our program may no longer needs certain data. We could just leave this garbage floating around forever, but, alas computers a finite amount of space to work with. It’s trivial to produce useful data; the more difficult task is figuring out when it’s no longer needed.&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>FFIs [Were] Hard</title>
        <published>2020-12-12T00:00:00+00:00</published>
        <updated>2020-12-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/passerine/ffi-hard/"/>
        <id>https://slightknack.dev/passerine/ffi-hard/</id>
        
        <content type="html" xml:base="https://slightknack.dev/passerine/ffi-hard/">&lt;blockquote&gt;
&lt;h2 id=&quot;note&quot;&gt;Note&lt;&#x2F;h2&gt;
&lt;p&gt;This post was written early on in Passerine’s development, before we had an FFI to interact with Rust. The FFI is still not fully finalized; most notably, as of writing this header, we need to still implement serde-style macros to easily interface with Rust datatypes, implement a module system to allow for external modules, and (eventually) allow for FFI interaction w&#x2F; Wasm modules for cross-platform FFI support.&lt;&#x2F;p&gt;
&lt;p&gt;It’s possible to add two numbers in Passerine now, don’t worry ;D&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;or-why-you-can-t-even-add-two-numbers-in-passerine-yet&quot;&gt;Or, why you can’t even add two numbers in Passerine yet.&lt;&#x2F;h2&gt;
&lt;p&gt;I figure someone will ask this question eventually, so I’m writing this response now. Foreign Functional Interfaces are hard. Not conceptually speaking — I could probably crank (an admittedly bad one) out in an afternoon — but in terms of designing a solution with an active admonition of traits Passerine strives to embody.&lt;&#x2F;p&gt;
&lt;p&gt;Above all else, Passerine strives to be a &lt;em&gt;concise&lt;&#x2F;em&gt; language, in terms of design and implementation. What does this mean? I mean concision on two fronts:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The language and it’s implementation should be simple. You should be able to keep the entire system in your head, and understand how everything fits together.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;The language and it’s implementation should be combinatorial. Although the language should be simple, it should feel as friction-less as possible. The answer to “Wait, can A and B work together like this?” should always be “Woah, I wasn’t expecting that to work, but it makes so much sense”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These goals are slightly conflicting. To build a system where everything composes together perfectly requires Good Architecture™ to be in place. Essentially, &lt;code&gt;MxN&lt;&#x2F;code&gt; problems need to be reduced to &lt;code&gt;Mx1, 1xN&lt;&#x2F;code&gt; problems wherever possible.&lt;&#x2F;p&gt;
&lt;p&gt;So right, back to Passerine. Why can’t you add you numbers yet?&lt;&#x2F;p&gt;
&lt;p&gt;Adding, like any other operation, is a function. There’s nothing too special about adding when compared to subtraction, multiplication, etc. – two operands in, one value out.&lt;&#x2F;p&gt;
&lt;p&gt;What’s my point? To simplify Passerine on the two fronts mentioned above, I’m opting to build an extensible FFI system into Passerine, through which I’ll implement performance critical parts of the standard library as well as common operations on data. Instead of defining an add operator in the language itself (which dispatches on type to perform the correct operation) Passerine will simply use FFI bindings to do this.&lt;&#x2F;p&gt;
&lt;p&gt;It’s critical that this FFI be fast, which is why it’ll bind to Rust, and be compiled against the language core. In the future, I hope that something like this is possible:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; &#x2F;ffi&#x2F;thing.rs
&lt;&#x2F;span&gt;&lt;span&gt;use passerine::{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, Data, Runtime};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;passerine&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;bind&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fbfdc;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;some_ffi_fn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffb964;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;: Data) -&amp;gt; Result&amp;lt;Data, Runtime&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#888888;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;elm&quot; style=&quot;background-color:#151515;color:#e8e8d3;&quot; class=&quot;language-elm &quot;&gt;&lt;code class=&quot;language-elm&quot; data-lang=&quot;elm&quot;&gt;&lt;span style=&quot;color:#888888;&quot;&gt;-- &#x2F;src&#x2F;main.pn
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;extern thing.some_ffi_fn
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#fad07a;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span&gt;some_ffi_fn (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#99ad6a;&quot;&gt;Hello, World&lt;&#x2F;span&gt;&lt;span style=&quot;color:#556633;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#cf6a4c;&quot;&gt;27.5&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is great because it allows for two things:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;New operations can be quickly added to passerine. It’ll be literally updating the lexer and parser, then writing a few lines of Rust&lt;&#x2F;li&gt;
&lt;li&gt;This FFI will allows users to define their &lt;em&gt;own&lt;&#x2F;em&gt; Rust functions that can be called from Passerine allowing for easier integration with existing Rust libraries.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Of course, before this is possible, a few things need to be done:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I need to flesh out pattern matching and the type system. This is what I’m working on now.&lt;&#x2F;li&gt;
&lt;li&gt;I need to start working on fibers, and concurrency in Passerine’s primary runtime environment, Aspen&lt;&#x2F;li&gt;
&lt;li&gt;I need to implement a module system, so using scoped FFI functions feels like using yet another Passerine module&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Of course, there are a few quality-of-life things I need to organize before I get started with this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;People have shown interest in trying out, developing, and contributing to Passerine. Up until this point, it’s been a one-man team, so I need to figure out how to more actively engage the community towards building something rather than just asking for feedback from time to time.&lt;&#x2F;li&gt;
&lt;li&gt;There is little tooling for Passerine. I’ve tossed together a bare-bones syntax highlighter I’m too embarrassed to publish, but I hope to polish it up and get it out the door soon.&lt;&#x2F;li&gt;
&lt;li&gt;Although I’ve had a lot of free time due to COVID-19 (the largest driver of Passerine’s development by far, ironically), I still have to attend classes, do homework, and fulfill other commitments I’ve made.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I appreciate the interest. As always, if you have any questions, comments, or suggestions, you know where to find me!&lt;&#x2F;p&gt;
&lt;p&gt;Have a nice one y’all,&lt;br &#x2F;&gt;
Isaac Clayton&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Mr. Covid 19</title>
        <published>2020-11-10T00:00:00+00:00</published>
        <updated>2020-11-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/gallery/covid/"/>
        <id>https://slightknack.dev/gallery/covid/</id>
        
        <content type="html" xml:base="https://slightknack.dev/gallery/covid/">&lt;p&gt;Towards the middle of the COVID pandemic, I was doing hybrid learning. We had a skeleton wearing a mask in the art room for whatever reason: an interesting subject for a sketch, so I sketched it.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>From 10° to 10⁻³</title>
        <published>2020-10-15T00:00:00+00:00</published>
        <updated>2020-10-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/gallery/ten/"/>
        <id>https://slightknack.dev/gallery/ten/</id>
        
        <content type="html" xml:base="https://slightknack.dev/gallery/ten/">&lt;p&gt;Inspired by the classic short film &lt;em&gt;Powers of Ten&lt;&#x2F;em&gt; from 1977, and the &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.nikonsmallworld.com&#x2F;&quot;&gt;&lt;em&gt;Nikon Small World Photomicrography Competition&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;, this piece combines two imagines of vastly different scales.&lt;&#x2F;p&gt;
&lt;p&gt;On the left is an human-scale image, existing on the order of magnitude of ten to the power of zero. It’s a young girl blowing bubbles; human faces are hard to pin down, so this piece was a practice in proportions. It’s quite visible that I still have a ways to go in this area… 😅&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;content&#x2F;oil.jpg&quot; alt=&quot;Oil on water, creating beautiful refraction patterns&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The right mimics a microscopic image, taken at ten to the power of negative three. It’s showing the reflection patterns caused as light bends through water at microscopic scales. The above image was used as a source of inspiration, and was &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.nikonsmallworld.com&#x2F;galleries&#x2F;2014-small-world-in-motion-competition&#x2F;oil-on-water&quot;&gt;taken by Dr. John Hart of CU Boulder&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Dr. Hart has done a lot of cool photomicrographic work; I highly recommend you &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.hart3d.com&#x2F;pages&#x2F;gallery-content&#x2F;index.html&quot;&gt;check out his website&lt;&#x2F;a&gt;, where he catalogs a lot of his work in detail.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Bridging the gap between the large and the minuscule are a series of bubbles that progressively transform into micro-droplets of oil on water. This was a large and fun project to work on, and I learned a lot throughout the planning and execution of this piece. Here’s a quick behind-the-scenes of the painting process:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;content&#x2F;ten-wip.jpg&quot; alt=&quot;Work in progress painting on the right, watercolors, tape, and other materials on the left&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Oh, and for those interested, here’s the original &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;0fKBhvDjuy0?t=13&quot;&gt;&lt;em&gt;Powers of Ten&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; video made back in 1977:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;0fKBhvDjuy0?t=13&quot;&gt;&lt;img src=&quot;&#x2F;content&#x2F;powers-of-ten.jpg&quot; alt=&quot;Powers of 10 on YouTube&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>All You Need are Coroutines and Pattern Matching</title>
        <published>2020-07-10T00:00:00+00:00</published>
        <updated>2020-07-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/passerine/all-you-need/"/>
        <id>https://slightknack.dev/passerine/all-you-need/</id>
        
        <summary type="html">&lt;blockquote&gt;
&lt;h2 id=&quot;note&quot;&gt;Note&lt;&#x2F;h2&gt;
&lt;p&gt;This is an old post I wrote a while back. My opinions on the subject have changed slightly but this post has historical merit. The date is not entirely accurate, this article coincides with around the time I started seriously implementing Passerine.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;abstract&quot;&gt;Abstract&lt;&#x2F;h2&gt;
&lt;p&gt;The average programmer just wants to write code [citation needed]. Language designers, on the other hand, want to &lt;em&gt;write&lt;&#x2F;em&gt; code, that is, programming languages. Ever since the first lisp interpreter sputtered through it’s first s-exp, there’s been a quest to build a ‘perfect’ programming language. The field of language design remains open, at least in the sense that a better language can always be built. &lt;strong&gt;No matter how hard you try, there is no language that won’t make you clarify your ideas.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Architecting Asynchronous Schedulers</title>
        <published>2020-07-10T00:00:00+00:00</published>
        <updated>2020-07-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/passerine/schedule/"/>
        <id>https://slightknack.dev/passerine/schedule/</id>
        
        <summary type="html">&lt;h1 id=&quot;note&quot;&gt;Note&lt;&#x2F;h1&gt;
&lt;p&gt;This post explores one design methodology for asynchronous schedulers in the context of a language that is maximally asynchronous, &lt;em&gt;i.e.&lt;&#x2F;em&gt; everything is executed asynchronously. This post is largely a reflection of trying to grapple with &lt;a rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tokio.rs&#x2F;blog&#x2F;2019-10-scheduler&#x2F;&quot;&gt;this blog post about Tokio’s scheduler&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>A Tiny Introduction to Parsers</title>
        <published>2020-07-08T00:00:00+00:00</published>
        <updated>2020-07-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Isaac Clayton
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://slightknack.dev/blog/parser/"/>
        <id>https://slightknack.dev/blog/parser/</id>
        
        <summary type="html">&lt;h1 id=&quot;a-question&quot;&gt;A Question&lt;&#x2F;h1&gt;
&lt;p&gt;The other day, someone asked how parsers worked on The Programmer’s Hangout Discord server. Here’s an an abridged version of my explanation.&lt;&#x2F;p&gt;
</summary>
        
    </entry>
</feed>
