journal.stuffwithstuff.com http://journal.stuffwithstuff.com/ Programming, languages, compilers, games, etc. en-us Sat, 14 Feb 2026 20:01:14 GMT Sat, 14 Feb 2026 20:01:14 GMT The Value of Things http://journal.stuffwithstuff.com/2026/01/24/the-value-of-things/ Sat, 24 Jan 2026 08:00:00 GMT [email protected] (Robert Nystrom) http://journal.stuffwithstuff.com/2026/01/24/the-value-of-things <p>One of the reasons I write is to help me organize my own mind. I have a compulsive need to <em>figure things out</em> and I&rsquo;ll lay awake at night shuffling sentences around in my head until it hangs together. Then I just have to try to get it all down in Markdown before it dissolves back into chaos.</p> <p>Like a lot of people these days, I am losing a lot of sleep over <span name="generative">LLMs</span> and generative AI. I mean that literally. I&rsquo;ve had nights where I tossed and turned worrying about whether AI is going to destroy a career I love or wreck modern society until the sun comes up.</p><aside name="generative"> <p>For brevity&rsquo;s sake, I&rsquo;ll use &ldquo;AI&rdquo; here mostly to mean &ldquo;generative AI&rdquo;. The stuff that LLMs and products like ChatGPT do. I think it&rsquo;s useful to distinguish that from other uses of machine learning like categorization and labeling. But here, all I care about is the generative stuff.</p></aside> <p>I keep hoping I&rsquo;ll sort it out in my head enough to reach some inner peace. I&rsquo;m not saying I will literally figure it out, like, for <em>the world</em>. But I keep hoping I can at least figure out my own relationship with AI.</p> <p>I&rsquo;m still not there, but I was able to pull one thread out of the tangled discourse around AI and get it to make sense in my head. I hope it will help you too. Or maybe this is all nonsense and you&rsquo;ll point out the myriad ways in which I&rsquo;m wrong. That&rsquo;s a possibility, but I&rsquo;m trying to be more courageous in my writing, so here I am.</p> <p>Anyway. Generative AI is about using machine learning to produce digital things. For AI to be a <span name="net">net good</span> for the world, at the very least, the things it produces must have value. But what does it mean for a thing to have &ldquo;value&rdquo;?</p><aside name="net" class="bottom"> <p>I say &ldquo;at the very least&rdquo; here because even if AI produces valuable things, it can still be a <em>net</em> bad. Societal effects and other externalities matter! But I haven&rsquo;t untangled any of that stuff in my head yet, so I&rsquo;ll ignore that here.</p></aside> <h2 id="utility"><a href="#utility">Utility<span class="anchor">#utility</span></a></h2> <p>The obvious answer is that a thing has value if it&rsquo;s useful. If it solves some material problem in the world or at least creates some joy, that&rsquo;s value. In the UX world, they call this <span name="utility">&ldquo;utility&rdquo;</span>: the thing a thing actually does.</p><aside name="utility"> <p>In UX, &ldquo;utility&rdquo; is used to distinguish from &ldquo;usability&rdquo;&mdash;how <em>easy</em> it is to do the thing.</p></aside> <p>An apple has utility. You can eat it and it will give you some calories, some hydration, a little fiber. If it&rsquo;s not an oxymoronically named Red Delicious, it might even bring you some delight. Material objects have a natural sort of utility from their physicality, but digital objects can have utility too.</p> <p>A while back, I took an interest in audio programming. I read a lot of articles online to learn some of the math and algorithms behind digital signal processing. That information is useful. Before I read those articles, I couldn&rsquo;t program an <a href="https://en.wikipedia.org/wiki/Frequency_modulation_synthesis">FM synth</a>. Now I can. That utility is just as real whether I got it from reading a classic article written by Julius O. Smith III, or from asking <span name="correct">ChatGPT</span>.</p><aside name="correct"> <p>There is a whole separate question about whether generative AI even <em>can</em> reliably produce good information. For this article, I&rsquo;m presuming a <a href="https://en.wikipedia.org/wiki/Straw_man#Steelmanning">steelman</a> AI. Presume that we have the best version of generative AI that does do what it&rsquo;s supposed to. Even then, how should we think about using it?</p></aside> <p>In both the physical and digital worlds, utility really does matter. One possible anthropological definition of &ldquo;technology&rdquo; is <em>anything</em> that enables humans to more efficiently generate utility. It&rsquo;s central to human progress.</p> <p>The reason I can go to a store and buy a bag of apples is because thousands of years of agriculture technology development means humans can very efficiently produce and transport food. And, you know, we need food to live. Food is cool.</p> <p>My career as a programming language designer is only possible because of all of the material I found online to learn how compilers and languages work. Hobbies like knitting, <a href="https://tinywir.es/">making music</a>, and cooking are deeply important to me. They&rsquo;re possible in large part from learning through YouTube videos. Those articles and videos were truly useful to me.</p> <p>Generative AI can help improve this. Here&rsquo;s a concrete example: I got curious about how AI is affecting software jobs outside of my weird tech company bubble so I started poking around job listing sites. I stumbled onto a software engineer position working for the Washington Department of Ecology. The listing says:</p> <blockquote> <p>We&rsquo;re looking for two journey-level developers who care about clean architecture, thrive in Agile teams, and see modern tooling&mdash;including AI-assisted development&mdash;as a way to work smarter, not riskier. You&rsquo;ll collaborate with product, architecture, security, and platform engineering partners to design secure, accessible, scalable applications that replace decades of legacy complexity.</p> <p>&hellip;</p> <p>Use AI-Assisted Tools Responsibly and Effectively</p> <ul> <li>Leverage AI tools for boilerplate, test generation, and safe refactoring.</li> <li>Validate AI-generated code for accuracy, security, and maintainability — no blind trust, no shortcuts.</li> <li>Share best practices with teammates adopting new AI workflows.</li> </ul> </blockquote> <p>It&rsquo;s hard for me to imagine a better morally justified use of AI for software development. This is a government office whose charter is to &ldquo;protect and sustain healthy land, air, water, and climate in harmony with a strong economy&rdquo;. They are literally working to make the world better for nature and humans.</p> <p>But as a government department, they are also really limited in their resources. If AI can make those two software engineers more productive, it can take the same tax dollars as input and produce a healthier world as output. That&rsquo;s hard to argue against.</p> <h2 id="meaning"><a href="#meaning">Meaning<span class="anchor">#meaning</span></a></h2> <p>When people get excited about AI and productivity, I think utility is what they have in mind. But utility is not the only kind of value a thing can have.</p> <p>A couple of years ago, I <a href="/2025/05/30/consider-knitting/">hand-knitted a scarf for my mother-in-law</a>. The scarf does have utility. It&rsquo;s a rectangle of fabric that keeps her neck warm. The Olympic Peninsula is a lovely corner of the country, but it&rsquo;s often a chilly one. A scarf made out of wet tissue paper would not have the same utility.</p> <p>I could have just gone to the store and bought her a scarf. Were I the kind of robotic rational actor who thinks only about maximizing their utility function when not sipping pour-overs in some culty rationalist commune outside of SF, then I would know that the optimal strategy is to use my software engineer skills to maximally turn my labor into cash and then use that cash to buy a scarf.</p> <p>The scarf I hand-made took dozens of hours to make. At even a modest West Coast software engineer salary, that number of hours would buy an extremely nice scarf. Maybe not <a href="https://nimble-needles.com/wool-and-tools/vicuna-wool-knitting-with-the-most-expensive-fiber-in-the-world/">vicuña</a>, but at least cashmere. Had I dropped a few hundred bucks on a scarf, would it have more value? Realistically, it would be a better scarf. Softer material, finer stitches. Certainly more <span name="fashion">fashionable</span>.</p><aside name="fashion"> <p>Fashion is social signalling. Social signals have utility and value too.</p></aside> <p>You and I know the answer is &ldquo;no&rdquo;. As any parent who lovingly clips their kid&rsquo;s objectively terrible art to the fridge knows, the value of a made thing is not entirely based on its merits. That scarf has value not because it&rsquo;s a great scarf but because I chose to spend an irreplaceable fraction of my life making it. It&rsquo;s a symbol of how much I care about her.</p> <p>In short, it has <em>meaning</em>. Meaning doesn&rsquo;t make an object more useful. It&rsquo;s not even transferrable. If my mother-in-law gave the scarf to you, its meaning would evaporate.</p> <p>But I bet that if you think about the objects that have the most value to you, your S-tier will be populated by things that excel mostly in meaning, not utility. The things that you would grab before running out of a burning house are the things with sentimental, not functional value.</p> <h2 id="source-of-meaning"><a href="#source-of-meaning">Source of meaning<span class="anchor">#source-of-meaning</span></a></h2> <p>You kind of know the answer intuitively, but I&rsquo;m trying to be clear in my thinking, so when I talk about objects having meaning&hellip; where does that meaning come from? Why do some objects have more meaning than others?</p> <p>I&rsquo;ve spent hundreds of hours knitting over the past couple of years, mostly making things for friends and family. I&rsquo;ve thought about this question a lot. I suspect that it comes down to spending time in service of another.</p> <p>Humans are sentient actors with agency and entire cognitive universes in our heads. But we are also animals made of meat and bones, and that flesh doesn&rsquo;t last forever. Every one of us has a finite amount of <em>things we will ever do.</em> &ldquo;Spend time&rdquo; is one of those stock phrases that has been sucked dry of semantic flavor over the years, but there is a profound metaphor there. When you choose to use some of your time making a thing for someone, <em>you don&rsquo;t get that time back</em>. It is spent.</p> <p><span name="matter">Choosing</span> to spend a fraction of our most precious resource for someone else is the strongest signal we can send that that person matters to us.</p><aside name="matter" class="bottom"> <p>If we want to put our philospher hat on and play <a href="https://en.wikipedia.org/wiki/Five_whys">five whys</a>, we might wonder <em>why</em> someone spending time on us is a thing we value. Why does someone burning their life&rsquo;s candle warm our heart?</p> <p>I believe it&rsquo;s because we&rsquo;ve evolved to be a social species and we&rsquo;re hard-wired to feel good when we think the tribe wants and values us.</p></aside> <p>I&rsquo;m in my late 40s. I hope I still have a lot of living left to do, but the odds are very good that I&rsquo;m past the mid-point of my lifespan. An increasing fraction of conversations with my friends revolve around our various medical tribulations. I can feel the finiteness of life in my <span name="bone">bones</span>, quite literally. When I think about what I want to do with my remaining allotment of life, the answer that resonates is taking care of and making things for the people I love.</p><aside name="bone"> <p>Post-traumatic osteoarthritis sucks.</p></aside> <h2 id="generating-meaning"><a href="#generating-meaning">Generating meaning<span class="anchor">#generating-meaning</span></a></h2> <p>Generative AI, when wielded deftly, can be an amazing tool for creating things with utility faster and more easily than you ever could before. But it can&rsquo;t generate meaning. The giant matrix of floating point numbers in a rack of GPUs in some data center <a href="https://www.reddit.com/r/MyBoyfriendIsAI/comments/1lixkb7/ai_relationships_are_not_healthy_my_story_lessons/">does not love you</a>.</p> <p>Another story: When my brother and I were growing up, we were really into movies. We made short videos (hilariously bad), learned how to do special effects make-up (actually tolerably good), and all sorts of stuff like that. We dreamed about growing up and becoming another pair of Hollywood brothers like the Zuckers or Coens.</p> <p>Many years later, as a birthday present, I wrote my brother a screenplay for a short horror film about a mythological siren. I toiled on it every night after the kids went to bed for weeks. It&rsquo;s one of my favorite gifts.</p> <p>I don&rsquo;t know if we&rsquo;ll ever get a chance to shoot it. We live on opposite sides of the country and he can&rsquo;t handle the gloom of Seattle any more than I can handle the politics of the South. It&rsquo;s likely this screenplay has zero utility. But it still has a ton of meaning because I sweated every single word in that stack of <a href="https://blog.quoteunquoteapps.com/standard-screenplay-format-the-writers-guide/">12-point Courier pages</a>.</p> <p>Today, with the help of <span name="stranger">ChatGPT</span>, I could probably put together a feature-length screenplay in a tenth of the time. It might even be an objectively better screenplay for a better movie. But <em>because</em> I made the screenplay in a tenth of the time thanks to ChatGPT&rsquo;s help, it would hold only a tenth of the meaning for my brother. If my hypothesis that meaning comes from time sacrifice is true, then by making us more productive, AI eliminates meaning.</p><aside name="stranger"> <p>I mean, if ChatGPT is <a href="https://www.reddit.com/r/StrangerThings/comments/1qas2q6/the_duffers_were_seen_opening_3_chatgpt_tabs/">good enough for the Duffer brothers</a>&mdash;another great pair of filmaking bros&mdash;who am I to judge?</p></aside> <h2 id="both-kinds-of-value"><a href="#both-kinds-of-value">Both kinds of value<span class="anchor">#both-kinds-of-value</span></a></h2> <p>Most objects, digital or material, have a mixture of both kinds of meaning. My favorite <span name="rice">gifts</span> to give or receive are those rare ones that are both thoughtful <em>and</em> useful.</p><aside name="rice"> <p>Many years ago, I had a friend over for dinner. The next time I saw him, he handed me a rice cooker. It wasn&rsquo;t a particularly expensive one or anything, but he saw the direction my cooking hobby was going and knew I would get a lot of use out of it. We&rsquo;ve used that thing a hundred times since.</p></aside> <p>An object carries with it the weight of time that was put into making it and has a certain heft from what you can do with it. I think of AI as a tool that can transmute that former kind of value into the latter. It drains some of the meaning out, but in return lets you make more things with utility for the same effort.</p> <p>There is nothing unique to AI about this. Any tool that increases efficiency has the same property. They make little <a href="https://www.michaels.com/product/knit-quick-knitting-machine-by-loops-threads-10480859">knitting machines</a> that make rows of stitches as easily as turning a crank. I could have made a scarf for my mother-in-law using that in a fraction of the time.</p> <p>Efficiency&mdash;the multiplier that determines how much effort is required to make an object&mdash;works sort of like a slider that lets us choose the ratio of utility and meaning the resulting thing has. Generative AI, assuming the claims are true, has the capability to radically move that slider. It can produce huge volumes of stuff with real utility. But that exact same ease implies that the results are all devoid of a sense of personal meaning.</p> <h2 id="two-tangents"><a href="#two-tangents">Two tangents<span class="anchor">#two-tangents</span></a></h2> <p>I don&rsquo;t want to take the idea that efficiency is a slider between meaningful-but-scarce and meaningless-plenty too far. It&rsquo;s a rough model. There are at least two problems with it:</p> <p>First, it suggests that you can make an object infinitely meaningful by just making the process arbitrarily hard. I could have knitted that scarf using two leftover take-out Chinese bamboo chopsticks. In the dark. One-handed. That would certainly have required a greater sacrifice of time (and sanity) on my part. But there&rsquo;s obviously a point of diminishing returns where people value the effort we put into making things but less so when we elect to be masochists.</p> <p>Also, when making things, we often derive personal joy from the process itself. Yes, I did sacrifice time to knit a scarf but&hellip; I <em>like</em> knitting. It wasn&rsquo;t an entirely miserable toil or something. I got to play with yarn.</p> <p>The high level point is just that the more we automate the process of making a thing, the less of ourselves we put into it. And an object with less of ourselves in it is often valued less by the person who receives it. That&rsquo;s all I&rsquo;m saying.</p> <h2 id="what-kind-of-value-do-you-want"><a href="#what-kind-of-value-do-you-want">What kind of value do you want?<span class="anchor">#what-kind-of-value-do-you-want</span></a></h2> <p>The tools we use to make things and the resulting productivity gives us a lever to control how much we prioritize getting a useful object out fast versus getting an object suffused with meaning. That suggests a way to decide when we should and shouldn&rsquo;t use AI.</p> <p>If you just need a thing that does a thing, then its utility is what matters. I want the Washington Department of Ecology to use my very limited tax dollars as effectively as they can to keep the Pacific Northwest the enchanting natural marvel that it is. If they can do that better using AI, then by all means they should do so.</p> <p>If you want to make a thing that has some emotional resonance or some connection to other people&mdash;whether that be a loved one, an audience, or humanity as a whole&mdash;then you don&rsquo;t want to suck out any of its meaning.</p> <p>I listen to a lot of electronic music while I work. In some sense, it is <span name="furniture">&ldquo;utility music&rdquo;</span>. I just want a vibe to help me tune out the world and focus. I can&rsquo;t even handle lyrics while I&rsquo;m programming or writing.</p><aside name="furniture"> <p>Erik Satie, the OG of ambient, described his compositions as <a href="https://en.wikipedia.org/wiki/Furniture_music">&ldquo;furniture music&rdquo;</a>. He described it in explicitly utilitarian terms:</p> <blockquote> <p>I think of it as melodious, softening the noises of the knives and forks at dinner, not dominating them, not imposing itself. It would fill up those heavy silences that sometime fall between friends dining together. It would spare them the trouble of paying attention to their own banal remarks. And at the same time it would neutralize the street noises which so indiscreetly enter into the play of conversation. To make such music would be to respond to a need.</p> </blockquote></aside> <p>I have to admit that the genres I listen to are the ones most amenable to being automatically generated by machines. I literally do listen to <a href="https://www.youtube.com/watch?v=RsW4z57yqis">generative ambient</a> which has been a little corner of the music world since well before &ldquo;generative&rdquo; glommed onto the word &ldquo;AI&rdquo;.</p> <p>Even so, I still find my listening experience more gratifying when the music was made by someone who actually gave a shit about it. Even though the relationship between artist and listener is vague and indirect, that connection matters to me. I know that when I make music, the thing that helps me push through all of the many frustrations of producing is the hope&mdash;however tentative&mdash;that at the end of it all, one day someone might put on a track of mine and find their day improved by it being the temporary soundtrack to their life.</p> <p>Those moments of connection between artist and audience are what I live for as a creative person. For those, I can&rsquo;t see generative AI as anything but harmful.</p> <h2 id="sometimes-yes-and-sometimes-no"><a href="#sometimes-yes-and-sometimes-no">Sometimes yes and sometimes no<span class="anchor">#sometimes-yes-and-sometimes-no</span></a></h2> <p>Thinking about the things I make and consume and the kinds of value they contain helps me sort out some of my extremely conflicted feelings around AI.</p> <p>I want to emphasize that I&rsquo;m only looking at this from the perspective of a single person&rsquo;s individual use of AI. The global effects of AI are just as if not more important. But I haven&rsquo;t sorted out my feelings around that to have any idea what to say here. Maybe I never will.</p> <p>But I can set that aside and imagine a world where I can run a generative AI completely locally on my machine. A world where I was the only person on Earth who had an AI. In that timeline, there would be almost no externalities to consider. <em>Even then,</em> I still want to be mindful of the consequences of using AI to help me make.</p> <p>In my own uses, I aim to focus on the places where I think AI can improve the efficiency of making utilitarian goods. And when I&rsquo;m making something where the human connection and meaning are important, I will try to put as much of myself and as little of the machine into it as I can.</p> Setting Up an SDL3 Mac App in XCode 16 http://journal.stuffwithstuff.com/2025/07/13/setting-up-an-sdl3-mac-app-in-xcode-16/ Sun, 13 Jul 2025 07:00:00 GMT [email protected] (Robert Nystrom) http://journal.stuffwithstuff.com/2025/07/13/setting-up-an-sdl3-mac-app-in-xcode-16 <p>This is mainly a note for my future self, but making it a blog post in case it helps anyone else. This weekend, I decided to try making a little SDL3 app on my new M4 MacBook Pro. It took me a few hours to figure out how to get XCode to play nice with the SDL3 framework given MacOS&rsquo;s paranoia around unsigned code and malware.</p> <p>Here&rsquo;s the summary for anyone whose Google searching takes them here. The instructions are fairly similar to <a href="https://wiki.libsdl.org/SDL3/README-macos#setting-up-a-new-project-by-hand">the SDL3 README for MacOS</a>:</p> <ol> <li> <p>Start up XCode. Choose &ldquo;File &gt; New &gt; Project&hellip;&rdquo;.</p> </li> <li> <p>Select &ldquo;macOS&rdquo;, then under &ldquo;Application&rdquo; choose &ldquo;App&rdquo;. <em>This step is important. Choosing &ldquo;Command Line Tool&rdquo; will <strong>not</strong> work.</em> Click &ldquo;Next&rdquo;.</p> </li> <li> <p>Give your app a name. For &ldquo;Interface&rdquo;, choose &ldquo;XIB&rdquo;. For &ldquo;Language&rdquo;, choose &ldquo;Objective-C&rdquo;. Click &ldquo;Next&rdquo;. Choose a place to save your project.</p> </li> </ol> <p>Now you have a vanilla &ldquo;Hello World&rdquo; Mac app. The next step is integrating SDL:</p> <ol> <li> <p>Download <a href="https://github.com/libsdl-org/SDL/releases">a release</a> of SDL. As I&rsquo;m writing this, the latest is 3.2.16. Download the corresponding <code>.dmg</code> file. Open it in Finder.</p> </li> <li> <p>Open <code>SDL3.xcframework/macos-arm64_x86_64</code> and copy <code>SDL3.framework</code> from there to somewhere in your XCode project&rsquo;s source tree. (I made a <code>lib</code> folder and put it there in mine.)</p> </li> <li> <p>In XCode, in the file explorer on the left, click the top project icon to open the settings for it. In the main panel on the right, click the icon for your app target under &ldquo;Targets&rdquo; to open the target settings.</p> </li> <li> <p>Under &ldquo;Frameworks, Libraries, and Embedded Content&rdquo;, click the &ldquo;+&rdquo; to add a new framework. In the popup that appears, click &ldquo;Add Other&hellip; &gt; Add Files&hellip;&rdquo;. Navigate to wherever you copied &ldquo;SDL3.framework&rdquo; and choose it.</p> </li> </ol> <p>Then let&rsquo;s use SDL and verify everything is working.</p> <ol> <li> <p>In <code>main.m</code>, comment out the entire <code>main()</code> function.</p> </li> <li> <p>Make a new <code>main.c</code> file and copy the contents of <a href="https://github.com/libsdl-org/SDL/blob/main/docs/hello.c">hello.c</a> from the SDL docs into it.</p> </li> <li> <p>Compile and run.</p> </li> </ol> <p>If that all works, you should see a delightful pixelated &ldquo;Hello World!&rdquo; fill your screen. Press any key to quit.</p> <p>Let&rsquo;s make sure the full iteration loop works. (Read below to see why I feel the need to verify this.):</p> <ol> <li> <p>In <code>main.c</code> inside <code>SDL_AppIterate()</code>, change the message string.</p> </li> <li> <p>Hit Command-R to run. You should see your updated message on screen.</p> </li> </ol> <p>There you go, you now have a working shell of a Mac app with using a local install of SDL3. You can probably delete the <code>main.m</code> file entirely and maybe some of the other generated files that XCode put in the new project, but I haven&rsquo;t figured that out yet.</p> <p>Now here&rsquo;s all the wrong stuff I tried first before I got that working&hellip;</p> <h2 id="attempt-1-download-a-release-and-use-the-xcframework"><a href="#attempt-1-download-a-release-and-use-the-xcframework">Attempt 1: Download a release and use the xcframework<span class="anchor">#attempt-1-download-a-release-and-use-the-xcframework</span></a></h2> <p>In XCode, I created a new &ldquo;CLI Command&rdquo; C app. That got me a &ldquo;Hello, world!&rdquo; up and running without any problems. Then I copied the contents of <a href="https://github.com/libsdl-org/SDL/blob/main/docs/hello.c">this SDL hello example</a> into my source file.</p> <p>As expected, that failed to compile because it couldn&rsquo;t find <code>SDL3/SDL.h</code>. So next is getting SDL linked in.</p> <p>The first thing I did was download <a href="https://github.com/libsdl-org/SDL/releases/tag/release-3.2.16">the 3.2.16 stable release installer</a>. I downloaded the DMG file and opened that. Then following the instructions, I dragged the entire <code>SDL3.xcframework</code> directory into the XCode project.</p> <p>That correctly added a reference to the Framework and got XCode to a point where it could resolve the include and compile the program. Great.</p> <p>But when I ran it, it failed at startup. The OS tried to dynamically load the SDL3 framework, but then balked because it thinks the framework isn&rsquo;t correctly signed and safe to use. The OS gave me a popup error with &ldquo;SDL3.framework Not Opened&rdquo; because &ldquo;macOS cannot verify that this app is free from malware&rdquo;.</p> <p>Crap.</p> <h2 id="attempt-2-embed-the-framework"><a href="#attempt-2-embed-the-framework">Attempt 2: Embed the framework<span class="anchor">#attempt-2-embed-the-framework</span></a></h2> <p>Poking around online, some people say this error can be fixed by embedding the SDL framework in my app and having XCode sign it when it embeds it. In XCode, in the target settings under &ldquo;Build Phases&rdquo;, there is a panel for &ldquo;Embed Frameworks&rdquo;. I dragged <code>SDL3.xcframework</code> under there and checked &ldquo;Code Sign On Copy&rdquo;.</p> <p>Alas, when I tried to compile, now I got a build error. It complained that two tasks in the build were trying to produce the same file. I&rsquo;ve lost the exact error message but it looked like one of the tasks was a simple file copy and the other was a &ldquo;ProcessXCFramework&rdquo; invocation. I&rsquo;m guessing the latter is the task that signs and embeds the framework.</p> <p>I tried basically every combination of build settings to try to fix this: removing the framework from &ldquo;Link Binary with Libraries&rdquo;, checking &ldquo;Copy only when installing&rdquo;, and a bunch of other things I don&rsquo;t remember. Nothing worked. Either I removed so much that the compiler couldn&rsquo;t find the framework at all, or it got the duplicate build file error.</p> <h2 id="attempt-3-dont-use-the-entire-sdl3xcframework"><a href="#attempt-3-dont-use-the-entire-sdl3xcframework">Attempt 3: Don&rsquo;t use the entire &ldquo;SDL3.xcframework&rdquo;<span class="anchor">#attempt-3-dont-use-the-entire-sdl3xcframework</span></a></h2> <p>I started watching <a href="https://www.youtube.com/watch?v=tRqgh8Xwe1E">this video</a> to see if it could help. He doesn&rsquo;t use the entire <code>SDL3.xcframework</code> bundle. Instead, he opens that, goes into <code>macos-arm64_x86_64</code> and copies the <code>SDL3.framework</code> framework out of there.</p> <p>I removed <code>SDL3.xcframework</code> from my XCode project and dragged <code>SDL3.framework</code> into it instead. That gave me something that compiled, but when I tried to run, it failed at load time with:</p> <pre class="highlight language-text">dyld[15605]: Library not loaded: @rpath&#47;SDL3.framework&#47;Versions&#47;A&#47;SDL3 Referenced from: &lt;...&gt; &#47;Users&#47;... Reason: tried: &#39;&#47;Users&#47;...&#47;Build&#47;Products&#47;Debug&#47;SDL3.framework&#47; Versions&#47;A&#47;SDL3&#39; (no such file)</pre> <p>In the target settings, I again added the framework (but this time <code>SDL3.framework</code> not <code>SDL3.xcframework</code> to &ldquo;Embed frameworks&rdquo;). For reasons that are entirely unclear to me, that worked. I hit Command-R and&hellip;</p><figure> <img class="framed" src="/image/2025/07/hello.gif"> <figcaption>Hallelujah.</figcaption> </figure> <p>All done, right? Not so fast. I made a tiny tweak to the code and tried to run it again. The compile immediately failed with:</p> <pre class="highlight language-text">&#39;SDL3&#47;SDL.h&#39; file not found</pre> <p>What? I literally just compiled and ran it. Just to make sure I wasn&rsquo;t crazy, I cleaned the build directory and compiled again. That worked! &ldquo;Hello World!&rdquo; popped up on screen again.</p> <p>Build again&hellip; same error.</p> <p>Clean build and then build. Works!</p> <p>So I can build <em>once</em> but then something in the build directly causes any subsequent compile to fail. The full error is:</p> <pre class="highlight language-text">Did not find header &#39;SDL.h&#39; in framework &#39;SDL3&#39; (loaded from &#39;&#47;Users&#47;...&#47;Index.noindex&#47;Build&#47;Products&#47;Debug&#39;)</pre> <p>So what I <em>think</em> is happening is that in the first clean build, it locates the framework inside my app&rsquo;s main directory. That framework does have the headers so the compiler can find what it needs.</p> <p>Then after that first build, the compiler starts using the framework that it copied into the build directory. But when I look in there, <code>Debug</code> is completely empty.</p> <h2 id="attempt-4-project-relative-deriveddata"><a href="#attempt-4-project-relative-deriveddata">Attempt 4: Project-relative DerivedData<span class="anchor">#attempt-4-project-relative-deriveddata</span></a></h2> <p>I hate when tools stuff built data in random system directories unrelated to the project I&rsquo;m working on because those files end up sitting around forever. So the next thing I did was go under &ldquo;Project Settings&rdquo; and changed the &ldquo;Derived Data&rdquo; to be &ldquo;Project-relative Location&rdquo;.</p> <p>I cleaned and built successfully. But, again, if I made a trivial code change and tried to build again, it failed. This time, though, there was actually something in <code>DerivedData/my_app/Build/Products/Debug/</code>.</p> <p>I can see an <code>SDL3.framework</code> directory under there. It contains <code>Versions/A/SDL3</code> (and some other stuff). So the library is there as expected. But there is no <code>Headers</code> directory under <code>Versions/A/</code>, nor is there a <code>Headers</code> symlink in <code>SDL3.framework</code> pointing to it.</p> <p>In theory, that&rsquo;s fine. In the compiled app, all that&rsquo;s needed is the library itself. But for reasons I don&rsquo;t understand, XCode is trying to use this semi-copied version of the framework for subsequent compiles instead of the original one in my project&rsquo;s source tree.</p> <h2 id="attempt-5-explicit-header-search-path"><a href="#attempt-5-explicit-header-search-path">Attempt 5: Explicit header search path<span class="anchor">#attempt-5-explicit-header-search-path</span></a></h2> <p>OK, if XCode can&rsquo;t find the headers because it&rsquo;s looking in the wrong place, maybe I can just tell it where to look. Under &ldquo;Search Paths&rdquo;, I added <code>lib/SDL3.framework/Headers</code> to &ldquo;Header Search Paths&rdquo;. (I put the framework under <code>lib</code> in my project tree, hence the <code>lib/</code> part.) Didn&rsquo;t help. Tried making it recursive. Nope.</p> <p>Maybe just <code>lib/SDL3.framework</code>? No.</p> <p>I&rsquo;m guessing the problem here is that the include paths look like <code>SDL3/SDL.h</code>, not just <code>SDL.h</code>, so I need to get XCode to understand that the headers are inside a framework to resolve the <code>SDL3/</code> part.</p> <h2 id="attempt-6-framework-search-path"><a href="#attempt-6-framework-search-path">Attempt 6: Framework search path<span class="anchor">#attempt-6-framework-search-path</span></a></h2> <p>OK, so maybe it&rsquo;s not a header search path. Instead, I tried adding <code>lib/</code> to &ldquo;Framework Search Paths&rdquo;. No help. Making it recursive didn&rsquo;t help. Neither did <code>lib/SDL3.framework</code>.</p> <p>I do a bunch of Googling and eventually stumble onto <a href="https://stackoverflow.com/questions/63630876/xcode-cant-find-the-header-path-after-build/63668035">this old StackOverflow post</a>. He specifically mentions only getting the error after the first build, so this is my exact issue. And the author apparently found a solution!</p> <p>&hellip;which they described only as &ldquo;It works know after certain changes!&rdquo;. So helpful.</p> <p>I eventually found <a href="https://developer.apple.com/forums/thread/750074">this Apple Developer forum thread</a>, which also sounds like my problem.</p> <h2 id="attempt-7-an-app-instead-of-a-cli-command"><a href="#attempt-7-an-app-instead-of-a-cli-command">Attempt 7: An App instead of a CLI Command<span class="anchor">#attempt-7-an-app-instead-of-a-cli-command</span></a></h2> <p>The forum thread suggests that the issue may have something to do with creating a &ldquo;CLI Command&rdquo; instead of an &ldquo;App&rdquo;. I&rsquo;ve always done the former in XCode because that seemed like the simplest way to get a vanilla C/C++ app up and running. In older versions of XCode/macOS, it worked fine. But maybe it doesn&rsquo;t play nice with frameworks in later versions?</p> <p>I trash the entire XCode project and create a new one. This time I choose &ldquo;App&rdquo; instead of &ldquo;CLI Command&rdquo;. I set the language to &ldquo;Objective-C&rdquo; (because that&rsquo;s the &ldquo;closest&rdquo; to C, I guess&mdash;I&rsquo;m winging it here). I set &ldquo;Interface&rdquo; to &ldquo;XIB&rdquo; based on <a href="https://wiki.libsdl.org/SDL3/README-macos">this README</a>.</p> <p>I run it and get an empty window, the &ldquo;Hello World&rdquo; of GUI apps. OK.</p> <p>I copy <code>SDL3.framework</code> out of <code>SDL3.xcframework/macos-arm64_x86-x64</code> into <code>lib/</code> in my new app&rsquo;s source tree. Then in XCode, in the &ldquo;General&rdquo; tab of the main target, under &ldquo;Frameworks, Libraries, and Embedded Content&rdquo;, I click the &ldquo;+&rdquo;. From there I choose &ldquo;Add Other&hellip;&rdquo;, &ldquo;Add Files&hellip;&rdquo;, and add <code>lib/SDL3.framework</code>.</p> <p>I admit to feeling a moment of anticipation. Will this actually work?</p> <p>I make a new <code>.c</code> file and paste in the <code>hello.c</code> SDL3 example code. I comment out <code>main()</code> in the <code>main.m</code> that XCode generated. Compile and&hellip; it runs! I&rsquo;ve got an SDL3 &ldquo;Hello World!&rdquo; on screen again.</p> <p>Now the real test&hellip; can I make a change and build again?</p> <p>Yes! It works! <em>Finally!</em></p> <p>As far as I can tell, I am up and running now. I didn&rsquo;t have to add SDL3 to my system framework path. (I didn&rsquo;t want to do that because ultimately, I want an app that users can just run without having to install SDL themselves.)</p> <p>So it looks like the failure mode was creating a &ldquo;CLI Command&rdquo; instead of an &ldquo;App&rdquo;. How on <em>Earth</em> would I be expected to know that?</p> Consider Knitting http://journal.stuffwithstuff.com/2025/05/30/consider-knitting/ Fri, 30 May 2025 07:00:00 GMT [email protected] (Robert Nystrom) http://journal.stuffwithstuff.com/2025/05/30/consider-knitting <p>Let&rsquo;s say that, like me, you are a person who stares at a computer and writes code for a living. As a straight male who grew up in a time where knitting was very strongly female coded, it for the most part <span name="stitch">never</span> occurred to me that knitting was a thing I could do and might enjoy. Regardless of your demographic categories and background, it&rsquo;s possible that you have also not really considered knitting.</p><aside name="stitch"> <p>In retrospect, this is pretty dumb. I <em>did</em> learn to cross-stitch when I was a kid and spent many hours doing that. I was mostly raised by my mom, so I learned a lot of crafty stuff.</p> <p>I also tried to learn how to crochet from my Grandmother a few times. But some combination of her being a mean old lady and me being left-handed made that not really work out.</p> <p>I made a friend in my twenties who was and is an avid knitter. I certainly admired her skills, but didn&rsquo;t think to ask her to teach me.</p></aside> <p>This article exists to get you to do so. Specifically, I&rsquo;ll try to convince you, one software person to another, why it might be a good fit for your life and brain. This is a pitch for knitting, but&mdash;for better or worse&mdash;an extremely nerdily argued one.</p><figure> <img class="framed" src="/image/2025/05/frog-and-toad.jpg" height="537.75"> <figcaption>Frog and Toad, from <a href="https://www.frogandcast.com/frog-and-toad-home">this delightful pattern</a>. I made these as Christmas presents for my daughters. </figcaption> </figure> <p>Before I start, note that when I say &ldquo;knitting&rdquo;, you can read that as any of the various <a href="https://en.wikipedia.org/wiki/Fiber_art">fiber arts</a>, including crochet, weaving, macramé, cross-stitch, etc. I talk about knitting here because that&rsquo;s the one closest to my heart and I strive to speak from the heart. You can make stuff out of string however you want. We are all fiber friends.</p> <h2 id="the-sense-of-touch"><a href="#the-sense-of-touch">The sense of touch<span class="anchor">#the-sense-of-touch</span></a></h2> <p>I love the aesthetics of programming. Sitting in a cool quiet room, techno thumping in my headphones, coffee mug next to me, while a neatly arranged field of glowing monospace glyphs stream across my screen. But there&rsquo;s one sense unmentioned in that sentence: touch.</p> <p>The sense we devote more neurons to than any other is curiously underutilized while pumping out code. It&rsquo;s no surprise that some programmers <a href="https://www.reddit.com/r/CustomKeyboards/">fetishize</a> <a href="https://www.reddit.com/r/MechanicalKeyboards/">keyboards</a>. It&rsquo;s just about the only part of programming that has any physical sensation <em>at all</em>.</p> <p>I got into knitting a few years after the pandemic. While I have a variety of hobbies, most still involve staring at a screen and maybe <a href="https://tinywir.es/">pushing some buttons and turning knobs</a>. When I wasn&rsquo;t doing those, I was staring at a screen for work, or staring at a screen for <a href="https://www.reddit.com/r/memes/comments/f6omv0/enjoying_some_good_screen_now/">not-work</a>.</p> <p>I don&rsquo;t know if I have a good way to explain how much my body craved tactile experience by the end of that. It&rsquo;s like my fingers ached. A deep hunger, but not for taste. I&rsquo;d wander around the house, driving my wife insane, unable to sit down and get comfortable. My body was just screaming at me to <em>do something.</em></p> <p>My youngest daughter had just picked up knitting, and she taught me. At first it was frustrating and annoying. But once I had the basics down, it was like a a deep sigh felt in my hands. Knitting is <em>so</em> touch-centered. Skilled knitters can knit without looking at their hands at all&mdash;touch alone is sufficient.</p><figure name="scarf"> <img class="framed" src="/image/2025/05/scarf.jpg" height="506"> <figcaption>Starting to work on a <a href="https://www.purlsoho.com/create/2020/02/07/slanting-stripes-scarf/">slanting stripes scarf</a> for myself.</figcaption> </figure><aside name="scarf"> <p>I wish you could reach into this photo and feel how unbelievably soft and squishy this scarf is. Wearing it feels like giving myself a warm gentle hug.</p> <p>If I ever become a billionaire, I&rsquo;m going to build an indoor swimming pool and fill it with Malabrigo Rios yarn.</p></aside> <p>There are so many different kinds of yarn to work with and they all feel different. Cotton is tough and firm, like twine. Wool is soft and springy, forgiving as you pull stitches open to work them. Superwash wool is smooth and glides off the needles. Non-superwash wool has this very slight stickiness to it that makes the resulting fabric feel solid and whole. Thin fingering weight yarn wraps tightly around your finger like a reminder knot. Working it is like performing delicate surgery. Squishy chunky wool spreads your fingers wide and works so quickly it&rsquo;s like fabric is spooling out of your hands on its own accord.</p><figure> <img class="framed" src="/image/2025/05/megan-hat.jpg" height="504.5"> <figcaption>The wool for this hat is a mixture of alpaca and sheep's wool. It's soft, fuzzy, and just the tiniest bit scratchy.</figcaption> </figure> <p>Even needles each have their own personality. Stiff grippy bamboo with its dull clack. Less worrisome to use because stitches don&rsquo;t slide off as easily, but harder work to push against the friction. Polished stainless steel where the stitches fly off the needles&mdash;good when you are done with those stitches but not so much otherwise.</p><figure name="dpn"> <img class="framed" src="/image/2025/05/frog-start.jpg" height="485"> <figcaption>An inauspicious start for our friend Toad.</figcaption> </figure><aside name="dpn"> <p>I knitted Frog and Toad using lacquered wood size 2 double-pointed needles. It&rsquo;s hard to get a sense of scale from this photo, but the needles are only six inches long. Like very fancy cocktail toothpicks.</p></aside> <p>Once you&rsquo;ve worked a few thousand stitches into your muscle memory, you can watch your fingers form stitches almost of their own accord, hypnotically. Right needle opens the stitch and slides in. Left finger wraps the yarn around the needle. Right finger grabs yarn. Right hand pulls the needle back out, a new stitch formed and transferred to the other needle. Over and over, like a meditation in the body.</p> <p>Knitting <em>feels good</em>. It is an intimate, constant reminder that we are a tool-using species with thousands of years of evolution giving us incredible dexterity and the emotional wiring to make us want to use it.</p> <h2 id="an-open-world-game-with-optimized-skill-curve"><a href="#an-open-world-game-with-optimized-skill-curve">An open world game with optimized skill curve<span class="anchor">#an-open-world-game-with-optimized-skill-curve</span></a></h2> <p>Of course, you could get much of that same tactile joy by driving to your nearest yarn store and wandering around the aisles jamming your fingers into every ball and skein of yarn they have. (An activity I certainly <em>also</em> do and highly recommend.) Knitting isn&rsquo;t just about having a hedonistic tactile experience. It is a skilled art with an unbelievably deep lore.</p> <p>I <a href="https://www.mobygames.com/person/102093/bob-nystrom/">used to be a game programmer</a>, and I tend to look at a lot of activities through the lens of game design. Games are interesting because they are user experience distilled to its essence. When you are, say, using a banking app to transfer some money into savings, there is a user experience in play. But there&rsquo;s also a <em>utility</em>, a real effect you are trying to have in the material world. You may use your banking app and be satisfied that it helps you save money even if the UX is trash.</p> <p>But a game, almost by definition, doesn&rsquo;t <em>do</em> anything &ldquo;out there&rdquo;. Aside from leaderboards and stuff, the point of a game is to be a low-stakes sandbox where you can play without, you know, accidentally deleting money out of your bank account. Because of that, it&rsquo;s a pure user experience. All experience and nothing else.</p> <p>Game designers have to make a user experience so good that you don&rsquo;t care that you aren&rsquo;t actually accomplishing anything at all with it. That makes the tools they use to think about gameplay experiences useful lenses to evaluate just about any kind of human endeavor.</p> <p>Two aspects (at least) are relevant to knitting.</p> <h3 id="linearity"><a href="#linearity">Linearity<span class="anchor">#linearity</span></a></h3> <p>A game is &ldquo;linear&rdquo; if there&rsquo;s only one path from beginning to end, one way to play. Don&rsquo;t particularly enjoy the desert level? Tough shit, you gotta get through it to get to the end. &ldquo;Nonlinear&rdquo; games let players choose among multiple paths to reach the end. &ldquo;Open world&rdquo; or &ldquo;sandbox&rdquo; games blow the gameplay wide open and let players go where they want when they want. The game may not even <em>have</em> an &ldquo;end&rdquo;.</p> <p>Knitting is an open world game. There are all sorts of objects you can make out of yarn, and all sorts of styles and techniques to make them. Don&rsquo;t like socks? Fine, you don&rsquo;t ever have to go through a &ldquo;sock making phase&rdquo; to graduate into what you really want to make. Find stranded colorwork too fiddly? There&rsquo;s intarsia or just buy a ball of self-striping yarn and let the yarn change colors for you. Is a sweater too big of a commitment? You can make hats forever.</p><figure name="hats"> <img class="framed" src="/image/2025/05/hats.jpg" height="504.5"> <figcaption>I've made a lot of hats. They're quick projects, only take a single skein of yarn, and almost always fit the wearer.</figcaption> </figure><aside name="hats"> <p>The yarn for these hats was a gift from my father and stepmother. I returned the favor by turning some of the yarn into these two hats and giving them back. This was my first time doing stranded colorwork. That means you are holding two yarns at the same time and switching between them frequently. Holding both yarns and the project and the two needles feels overwhelming, but once you get the hang of it, it&rsquo;s very satisfying being able to switch between the colors at each stitch. Like making analog pixel art.</p></aside> <p>Once you get past the very basics of getting loops on the needles and making stitches, knitting very rarely forces you to slog through something you don&rsquo;t want to do in order to reach some other goal. Except weaving in ends, I guess, which is kind of a chore. But, honestly, it&rsquo;s not that bad.</p> <h3 id="skill-curve"><a href="#skill-curve">Skill curve<span class="anchor">#skill-curve</span></a></h3> <p>A skill curve is sort of like a learning curve. It&rsquo;s an imaginary graph of how much effort it takes to reach greater and more rewarding levels of skill. Some skill curves are steep at the beginning and then flatten out once you&rsquo;re over the hump. When you first start playing guitar and don&rsquo;t have the hand strength, callouses, or dexterity to form chords, it&rsquo;s <em>really</em> hard. But after a few days you can get the basics down. Then it&rsquo;s pretty easy to learn more and more chords after that.</p> <p>Other skill curves start shallow and get steeper. You can learn chess in a day and have fun playing it, but as you get more serious about it, each incremental increase in skill requires a greater commitment to studying the theory of the game.</p> <p>Knitting has a marvellously smooth, user-controllable skill curve. There&rsquo;s a small hump at the beginning. It does take a little while to figure out how to hold the needles, control the tension of the yarn with your fingers, and get your hands to work in concert to make stitches. It feels like you&rsquo;re making a shadow puppet of a sewing machine. Your initial experience will be frustrating.</p> <p>But you can push through that in an hour or so. In a day, you can learn a basic way to cast stitches onto the needles, make knit and purl stitches, and then cast off to finish the work. With just those, you can make scarves and dishcloths. You are a knitter.</p><figure> <img class="framed" src="/image/2025/05/toad-trunks.jpg" height="504.5"> <figcaption>Working on a swimsuit for Toad. The legs are stockinette stitch, just simple knit stitches over and over, switching colors every few rows. </figcaption> </figure> <p>Then the world is your oyster. There are <a href="https://www.ravelry.com/">thousands of patterns out there</a>, each listing the techniques required. You can pick ones well inside your comfort zone and grow your skills slowly. Or you can challenge yourself to learn a bunch of techniques at once. There are dozens of tiny little independent tricks to learn, each it&rsquo;s own little merit badge serotonin hit: long-tail cast on, increases and decreases, cables, etc. So many fun different ways to form stitches. Each is one bite-sized lesson and no matter how many or few you want to chew on at a time, there is a project out there that will satisfy your appetite.</p><figure> <img class="framed" src="/image/2025/05/holding-hands.jpg" height="537.75"> <figcaption>I learned how to do a picot bind-off to make these tiny frog fingers.</figcaption> </figure> <p>It&rsquo;s not just the steepness of the curve, but also its height. Some skill curves top out early. I suspect the world&rsquo;s greatest kazoo player is not profoundly better at kazoo than I am. Others seem to have no limit, like the world&rsquo;s best violinists or Go players.</p> <p>Friend, the knitting lore goes <em>deep</em>. People have been developing this artform for literally over a thousand years. Knitters have sat there, brain semi-idle, while their fingers worked yarn for millions of hours. They had <em>plenty</em> of time to invent all sorts of crazy ways to tangle yarn up. You could knit full-time for the rest of your life and never run out of new things to learn. Knitting will never stop rewarding you.</p><figure name="chicken"> <img class="framed" src="/image/2025/05/chicken.jpg" height="504.5"> <figcaption>This pattern taught me short rows, wrap-and-turns, and cable cast-ons. </figcaption> </figure><aside name="chicken"> <p>Stuffed animals are typically made using crochet which more easily handles complex organic shapes, but there are some patterns for knitted ones too, like the famous <a href="https://www.ravelry.com/patterns/library/emotional-support-chicken">Emotional Support Chicken</a> giving you the side-eye here.</p></aside> <h2 id="structured-but-not-a-game"><a href="#structured-but-not-a-game">Structured but not a game<span class="anchor">#structured-but-not-a-game</span></a></h2> <p>To be clear, I&rsquo;m talking about knitting and videogames <em>as a metaphor</em>. I don&rsquo;t think of knitting as a game to win. Like a lot of programmers, I am prone to pointing my dumb optimizing brain at random activities and trying to min-max the shit out of it. Ask my wife how many ways to make coffee I have tried to find the optimal effort/reward ratio.</p> <p>When I knit, I do try to knit efficiently. If it takes 10,000 stitches to finish a single scarf, it behooves one to put some thought into the process. At the same time, knitting isn&rsquo;t just a pointless exercise to scratch my gamification itch. It is a real artform, and I am making real objects.</p><figure> <img class="framed" src="/image/2025/05/bob-scarf.jpg" height="504.5"> <figcaption>Thousands of stitches, spooled out from time into space.</figcaption> </figure> <p>For me, knitting strikes a good balance between structure and unstructure. I <a href="https://tinywir.es/">like making music</a> and when it goes well, it&rsquo;s very rewarding. But sometimes I sit down and every melody that comes out sounds like a knock-off NES game (and not in that cool chiptune way). Or I&rsquo;ll spend an entire evening working on the drum mixing and at the end I can&rsquo;t tell if I made things better or worse. It&rsquo;s <em>too</em> unstructured.</p> <p>When I sit down to knit, I might make a mistake that needs unwinding and fixing. But for the most part, I can be confident that an hour spent knitting will get me closer to a beautiful finished object. It provides a reliable serotonin hit of &ldquo;I&rsquo;m making progress&rdquo;. But it&rsquo;s not so rigidly structured that it triggers my optimizing brain into sucking the joy out of it by turning it into math homework.</p> <h2 id="a-time-and-a-space"><a href="#a-time-and-a-space">A time and a space<span class="anchor">#a-time-and-a-space</span></a></h2> <p>A particularly nice property of knitting is that it is able to provide that reliable gratification while accommodating all of the other complexities in my life. It takes very little time to make progress knitting. Unlike, say, painting, there&rsquo;s almost no set up at the beginning of a session or clean up at the end. When I pick up the kids from school, I can get ten minutes of knitting in while I wait for the bell.</p><figure> <img class="framed" src="/image/2025/05/in-car.jpg" height="490.5"> <figcaption>Working on a blanket square while I wait for the kids.</figcaption> </figure> <p>Nor does it take up much space. A zip-loc bag with a ball of yarn and two needles is basically all you need, which is always in my backpack when I get on an <span name="flight">airplane</span> now.</p><aside name="flight"> <p>Conveniently, the TSA <a href="https://www.tsa.gov/travel/security-screening/whatcanibring/items/knitting-needles">specifically allows knitting needles on flights</a>. Note that this only applies to domestic flights in the US. Other countries have their own rules, though I haven&rsquo;t had any trouble bringing knitting to Denmark or Costa Rica.</p> <p>The fact that the TSA is explicitly fine with you bringing a satchel full of foot-long sharpened metal spikes onto a plane as long as its accompanied by some string really says something about how meaningless these security rules are.</p></aside> <p>Knitting expands and contracts around not just physical space, but headspace too. Had a long day and want something mindless to help you unwind? Slap together a <a href="https://www.ravelry.com/projects/munificent/slanting-stripes-scarf">garter stitch scarf</a> and just do the same stitch over and over again. Stressed out by work or grieving a loss and need something consuming to take your mind off it? Start a lacework or cable knit project and the chart and counting won&rsquo;t leave room to think about anything else.</p><figure> <img class="framed" src="/image/2025/05/chocolate-shop.jpg" height="504.5"> <figcaption>Working on a little dishcloth while sipping hot cocoa.</figcaption> </figure> <p>Whatever logistical or mental capacities you have, there is a knitting project that will tuck neatly into it.</p> <h2 id="and-then-at-the-end"><a href="#and-then-at-the-end">And then at the end<span class="anchor">#and-then-at-the-end</span></a></h2> <p>So far, I&rsquo;ve been talking about knitting as an <em>activity</em>. A personal hobby to kill time for your own joy. Kill time it does, but knitting isn&rsquo;t just about whiling away the hours. It&rsquo;s not playing solitaire or binging a TV show for the fifth time.</p> <p>As yarn spools through your fingers and the hands twirl around the clock, an actual physical, beautiful object emerges at the end. Well, your first couple of objects may not be so beautiful. But even the lumpiest knitted scarf is imbued with something increasingly elusive these days: <em>care</em>.</p> <p>The first real thing I knitted was a scarf for my mother-in-law. In retrospect, I can&rsquo;t say it&rsquo;s a great scarf. Kinda cheap acrylic yarn. Not really her color. 4x4 rib was about all I could handle complexity-wise at the time, and it means the scarf tends to bunch up on itself. But when she opened the package on Christmas and saw it, her eyes teared up. Mine are tearing up now writing this.</p><figure> <img class="framed" src="/image/2025/05/mimi-scarf.jpg" height="448.25"> <figcaption>The scarf.</figcaption> </figure> <p>Because regardless of how good the object itself is, it is an inarguable testament to the fact that I chose to spend dozens of quiet hours making stitch after stitch, all the while thinking about her and how much she means to me. A fraction of my life&rsquo;s wick that I burned for her and no one else.</p> <p>In a world where so many seem to want to get more and more out of less and less, to automate and AI-ify everything until an infinite content firehose is blasting into every orifice of every consumer, hand knitting to me is the antidote. An acknowledgement that all we really have is time and thus there is no gift more precious than spending it on someone.</p> <p>Also, once you finish a project, you get to buy more yarn. Because, if I&rsquo;m honest, a little consumption feels kinda nice too.</p> <h2 id="ok-im-sold"><a href="#ok-im-sold">OK, I&rsquo;m sold<span class="anchor">#ok-im-sold</span></a></h2> <p>Anyway, this is what knitting means to me. Which, now that I read all this, is a lot more than I realized. If that didn&rsquo;t pique your interest, fine. It&rsquo;s not for everyone. I do hope you find something out there to spend your time on that provides as much joy as knitting does to me. You deserve that.</p> <p>If this did make you want to give knitting a try, you&rsquo;re probably wondering what next. Fortunately, there are, like, a million &ldquo;learn how to knit&rdquo; tutorials out there. One of the actually marvellous things about living in the world today is good access to lots of videos, and knitting is an activity that&rsquo;s <em>really</em> hard to convey in book form. You kind of need to watch someone&rsquo;s hands. Learning from someone in person is best, but if you don&rsquo;t have that, YouTube is a pretty good substitute.</p> <p>Keep in mind that everyone&rsquo;s <span name="hands">hands</span> are different! There are many ways (a handful, heh heh) to hold the needles and form stitches because our anatomy and the texture of our skin varies so much. Watch a few videos and don&rsquo;t worry if what works for them doesn&rsquo;t work for you. Eventually, you&rsquo;ll find one that does.</p><aside name="hands"> <p>I knit &ldquo;Continental style&rdquo;. That means I hold the working yarn with my left hand and make stitches with my right. In English style knitting, the right hand both holds the yarn and makes stitches. That works well if all of your coordination is in that hand. But as a lefty, I find Continental takes better advantage of the dexterity in both of my hands.</p> <p>Even though I&rsquo;m left-handed, I knit the same way a right-handed person does. I don&rsquo;t mirror patterns or make stitches from left to right. If you are sinistral as well, I recommend still learning right-handed. It doesn&rsquo;t make that much of a difference in terms of dexterity, and it&rsquo;s much easier if you don&rsquo;t have to try to mentally mirror every pattern, instruction, and video you watch.</p></aside> <p>Expect to be challenged and frustrated at first. There&rsquo;s a lot to going on all at once: controlling the tension of the working yarn, keeping the stitches from the previous row on the left needle, keeping the new stitches on the right needle, forming new stitches. This may be one of the first times you&rsquo;ve used this many of your fingers doing <em>different</em> things all at once.</p> <p>I promise that if you&rsquo;re patient with yourself and give it a few tries, you will get over the hump. Once you can knit a swatch of garter stitch, everything else will come naturally over time.</p> <p>So go your local craft store, buy a cheap pair of size 7 needles, a ball of worsted (i.e. medium) weight acrylic or wool yarn in a color you think is pretty, and give it a try. The worst that can happen is you&rsquo;ll waste a few bucks. If you&rsquo;re lucky, you might end up making your mother-in-law cry (in a good way).</p> Access Control Syntax http://journal.stuffwithstuff.com/2025/05/26/access-control-syntax/ Mon, 26 May 2025 07:00:00 GMT [email protected] (Robert Nystrom) http://journal.stuffwithstuff.com/2025/05/26/access-control-syntax <p>I&rsquo;m still tinkering on a <a href="/2023/01/03/type-checking-if-expressions/">scripting language for my hobby fantasy console project</a>. I&rsquo;m ashamed to admit this, but up to this point, the language had absolutely no notion of modules. Literally every source file is dumped into one big global namespace and compiled together.</p> <p>I always planned to have some sort of module system. I just hadn&rsquo;t figured it out yet because I had other, harder <a href="/2023/08/04/representing-heterogeneous-data/">language design problems</a> to solve. I assumed that the module system mostly didn&rsquo;t interact with other language features, so I could kick it down the road for now.</p> <p>That was true until it wasn&rsquo;t. I&rsquo;ve been beating my head against the wall around generics for&hellip; oh God I just checked the Git history and it&rsquo;s three years now. I still don&rsquo;t have that pinned down. Parametric types are hard.</p> <p>Anyway, one of the approaches I&rsquo;m exploring <em>does</em> get tangled up in modules and scoping so now I have to figure modules out. This post is about one little syntax design question I ran into: <em>how do you distinguish public and private declarations?</em></p> <h2 id="a-basic-module-system"><a href="#a-basic-module-system">A basic module system<span class="anchor">#a-basic-module-system</span></a></h2> <p>Since my language is a scripting language, my ambitions for the module system are pretty minimal. Think more like Python or Dart than Java or C#.</p> <p>Every file has its own top-level scope that isn&rsquo;t shared with others. If you want to access top-level declarations from another file, you import that file. That makes its top-level declarations available in the importing file.</p> <p>Of course, a module might have some declarations that are only for its own internal use and should <em>not</em> be made available when you import it. A module should be able to encapsulate parts of its implementation. Thus, I need a way for users to indicate which declarations are private and which are public.</p> <h2 id="what-other-languages-do"><a href="#what-other-languages-do">What other languages do<span class="anchor">#what-other-languages-do</span></a></h2> <p>Every language out there has <em>some</em> kind of module system and an ability to control access (though it did take JavaScript about 20 years to get there, bless its heart). In some sense, it&rsquo;s a solved problem. But they don&rsquo;t all solve it the same way, especially if you dig into some of the more obscure corners of the language world. Let&rsquo;s go on a tour&hellip;</p> <h3 id="modifier-keywords"><a href="#modifier-keywords">Modifier keywords<span class="anchor">#modifier-keywords</span></a></h3> <p>The approach you probably already have in mind is modifiers before declarations. In Java, C#, PHP and others, that&rsquo;s <code>public</code>, <code>private</code>. Maybe also more specific ones like <code>protected</code>, and <code>internal</code>.</p> <p>It&rsquo;s clear, explicit, and gets the job done. It lets you support a large number of flavors of access control if you need. It&rsquo;s also extremely common, so easy for users coming to a new language to pick up.</p> <p>The flip side is that it&rsquo;s quite verbose. Java is reviled for being too boilerplate-heavy and wordy, and I believe that having <code>private</code> and <code>public</code> scattered throughout every single file is a major contributor to that.</p> <p>Picking the wrong defaults doesn&rsquo;t help. I used to program in C# professionally for several years and in that time, I can&rsquo;t recall <em>ever</em> wanting a member to have <code>internal</code> access, which is the default. So I had to write <code>public</code> or <code>private</code> on basically everything. I think the same is true in Java land.</p> <p>Rust improves the situation by having a shorter modifier, <code>pub</code>, and picking what is arguably the right default for a language designed for Serious Programming: private.</p> <h3 id="modifier-sections"><a href="#modifier-sections">Modifier sections<span class="anchor">#modifier-sections</span></a></h3> <p>C++ has a strange but perhaps underappreciated spin on modifiers. It has access modifier keywords, but they apply to <em>all subsequent declarations</em>. This lets you write a keyword once and apply it to a whole bunch of things. That really does cut down the verbosity.</p> <p>The price to pay is that it also makes the language curiously context-sensitive. If you&rsquo;re ever defined a preprocessor macro that inserts code in a class and had to be careful <em>where</em> in the class you called the macro, you&rsquo;ve run into this.</p> <p>Access control sections mean that you can&rsquo;t look at a single declaration and know what its access is. You have to know what section contains the declaration.</p> <p>There&rsquo;s also the funny historical thing where members in struct default to public and members in classes default to private. C++ is <em>weird</em>. Like a house built on the back of some Eldritch being whose architecture and plumbing reveals the unholy foundation it is mated with.</p> <p>For presumably less Eldritch reasons, I believe Ada takes a similar approach.</p> <h3 id="sigils-in-the-names"><a href="#sigils-in-the-names">Sigils in the names<span class="anchor">#sigils-in-the-names</span></a></h3> <p>Modifiers are clear but verbose. If you want something more syntactically economical, why not encode the access control directly in the name of the declaration itself? That&rsquo;s what Python, Go, and Dart do.</p> <p>Python&rsquo;s system is a mixture of informal and language supported. A leading underscore in a name doesn&rsquo;t <em>prohibit</em> it from being used outside of the module but it sends a signal to the user that they <em>shouldn&rsquo;t</em> use it. Sort of &ldquo;velvet rope&rdquo; security. If a class member starts with <em>two</em> leading underscores, then it really is private. The language will name mangle it to make it inaccessible.</p> <p>In Go, if a declaration&rsquo;s name starts with a capital letter, it&rsquo;s public. Otherwise, it&rsquo;s private. Because Go allows any Unicode letter in identifiers, the definition of uppercase <a href="https://en.wikipedia.org/wiki/Unicode_character_property#Casing">is not trivial</a>. Wikipedia tells me there are 1,858 uppercase letters and 2,258 lowercase letters that can be used in identifiers in Go. Fun!</p> <p>Dart only allows ASCII in identifiers and follows Python: If an identifier starts with <code>_</code>, it&rsquo;s private. Otherwise it&rsquo;s public. Unlike Python, this is strictly enforced by the language. Private names are completely inaccessible outside of the library where the declaration appears.</p> <p>I don&rsquo;t have enough Python experience to have an opinion there aside from finding <code>__</code> pretty long. I work full-time on Dart and have only tinkered in Go so I&rsquo;m highly biased, but Go&rsquo;s approach has always felt strange to me. I&rsquo;m sure it&rsquo;s mostly habit, but my monkey brain really wants case to be used to distinguish kinds of declarations: leading capitals for types and lowercase for functions. Seeing types with lowercase names or functions with capital names gives me the same squishy feeling in my gut as watching <a href="https://en.wikipedia.org/wiki/The_Polar_Express_(film)">The Polar Express</a>. Dumb subjective biases aside, though, it seems to work fine in practice.</p> <p>I&rsquo;m much more used to Dart&rsquo;s style. I don&rsquo;t love it, but, eh, it gets the job done.</p> <p>These approaches both have the advantage of being very terse. No modifiers, no additional reserved words. There are two strikes against them:</p> <ul> <li> <p><strong>They&rsquo;re obscure.</strong> If you&rsquo;re new to Java and you see the word <code>private</code>, you probably don&rsquo;t know what it does, but you can guess it has something to do with &ldquo;privacy&rdquo;. With Go, if you see that some names are capitalized and some aren&rsquo;t, that tells you absolutely nothing about what&rsquo;s going on. Historical baggage? Weird personal preference? Maybe the author is German and prefers capitalizing nouns?</p> <p>Likewise, if you&rsquo;re skimming some Dart code, why do some of the names start with <code>_</code>? Do you pronounce that when you say the name out loud?</p> <p>You just have to be told what&rsquo;s going on because the language&rsquo;s own syntax doesn&rsquo;t guide you towards an explanation.</p> </li> <li> <p><strong>They show up at the use sites.</strong> Access control is a property of a <em>declaration</em>. So, in principle, any syntax for specifying it should only appear at that declaration. When you go to use a name, either you can access it (in which case no syntax is necessary) or you can&rsquo;t (in which case there&rsquo;s nothing you could say). But with Go and Dart, every place you mention the name also carries the access control.</p> <p>In theory, this could be a major problem if you want to <em>change</em> the access control of a declaration. You have to fix the identifier at every single use site! In practice, this doesn&rsquo;t actually cause much pain. If a name is going from private to public, then every existing use site is already confined to one file, so you can rename those without affecting the rest of the program. If an identifier is going from public to private, again all uses must already be confined to that file or you <em>can&rsquo;t</em> make it private without actually breaking things.</p> </li> </ul> <p>I used to say that if I had a time machine, I would go back and change Dart to not use <code>_</code> for privacy. But I have mellowed on that opinion over the years and now I&rsquo;m not sure. I don&rsquo;t like how it shows up at every use site, but it <em>is</em> terse, which is nice.</p> <h3 id="export-manifests"><a href="#export-manifests">Export manifests<span class="anchor">#export-manifests</span></a></h3> <p>Modifers put access control at the declaration. Sigils put it at the declaration and the use sites. The third major approach puts it at <em>neither</em>.</p> <p>Someone will tell me this generalization is wrong but I think most functional languages including the ML family and Lisps (including Scheme) have some kind of separate syntax for listing <em>exports</em>. Within a module, you declare all the functions and types you want without worrying about modularity. Then at the top of the file, the language gives you some dedicated syntax to list the things that should be made visible outside of the module.</p> <p>For example, in Scheme, the syntax for defining a module looks like:</p> <pre class="highlight language-scheme"><span class="p">(</span><span class="i">library</span> <span class="i">library-name</span> <span class="p">(</span><span class="i">export</span> <span class="i">list-of-names-to-export...</span><span class="p">)</span> <span class="i">declarations-in-library...</span><span class="p">)</span></pre> <p>SML and its spawn have a notion of a <a href="https://smlhelp.github.io/book/docs/start/module-syntax">signature</a> which is declared separately from the module. You could also argue that header files in C and C++ are this pattern.</p> <p>A cool thing about export manifests is that they keep all the access control logic in one place. When you&rsquo;re writing declarations, you don&rsquo;t have to worry about access control then. You just write declarations. As a <em>user</em> of a library, you can look at just the export section to see what the module lets you do without digging in to the implementation at all.</p> <p>It does a good job of <em>firmly</em> separating interface from implementation.</p> <p>On the other hand, it&rsquo;s <em>quite</em> verbose. You end up saying the name of every exported declaration twice. Maybe even the full type signatures too. Since the export manifest is separate from the declarations, they have to be manually kept in sync. Rename an exported function and you have to remember to rename it in the manifest too.</p> <p>I ragged on JavaScript for not having its act together, but I gotta say when they did add modules, they came up with a clever blended approach. You can use <code>export</code> as a modifier right on a declaration:</p> <pre class="highlight language-javascript"><span class="k">export</span> <span class="k">function</span> <span class="i">area</span><span class="p">(</span><span class="i">width</span><span class="p">,</span> <span class="i">height</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="i">width</span> <span class="o">*</span> <span class="i">height</span><span class="p">;</span> <span class="p">}</span></pre> <p>But you can also use the <code>export</code> keyword to export a set of declarations that are already declared elsewhere:</p> <pre class="highlight language-javascript"><span class="k">export</span> <span class="p">{</span> <span class="i">area</span> <span class="p">};</span> <span class="c">// Code...</span> <span class="k">function</span> <span class="i">area</span><span class="p">(</span><span class="i">width</span><span class="p">,</span> <span class="i">height</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="i">width</span> <span class="o">*</span> <span class="i">height</span><span class="p">;</span> <span class="p">}</span></pre> <p>Given that modules were added later to the language, I&rsquo;m guessing this makes it easier to add modularity to a large existing JS library.</p> <h3 id="sigils-at-the-declaration"><a href="#sigils-at-the-declaration">Sigils at the declaration<span class="anchor">#sigils-at-the-declaration</span></a></h3> <p>In poking around the annals of language history, I found one last approach in Niklaus Wirth&rsquo;s magnum opus language <a href="https://oberon.org/en">Oberon</a>.</p> <p>In Oberon, top-level declarations are private by default and only accessible to the containing module. If you want to make a declaration public, you mark the declaration with an asterisk:</p> <pre class="highlight language-oberon"><span class="k">MODULE</span> <span class="i">Hello</span><span class="p">;</span> <span class="k">IMPORT</span> <span class="i">Out</span><span class="p">;</span> <span class="k">PROCEDURE</span> <span class="i">SayHello</span><span class="o">*</span><span class="p">;</span> <span class="k">BEGIN</span> <span class="i">Out</span><span class="p">.</span><span class="i">String</span><span class="p">(</span><span class="s">&quot;Hello World!&quot;</span><span class="p">);</span> <span class="i">Out</span><span class="p">.</span><span class="i">Ln</span><span class="p">;</span> <span class="k">END</span> <span class="i">SayHello</span><span class="p">;</span> <span class="k">END</span> <span class="i">Hello</span><span class="p">.</span></pre> <p>Here, the <code>*</code> after <code>PROCEDURE SayHello</code> means that <code>SayHello</code> is public. Note that this marker is only at the declaration. It&rsquo;s not part of the name. At callsites, the name is just <code>HelloWorld</code>.</p> <p>This has the same inscrutability that Python, Go, and Dart have. If you&rsquo;re a new Oberon programmer and you see <code>*</code>, you have no idea what it means. Fortunately, there are no new Oberon programmers, so this is not a problem in practice.</p> <p>This approach has the advantage of only appearing at the declaration. You don&rsquo;t have to repeat the sigil every time you use the thing.</p> <h2 id="any-syntax-at-all"><a href="#any-syntax-at-all">Any syntax at all?<span class="anchor">#any-syntax-at-all</span></a></h2> <p>I&rsquo;m sure there are interesting tweaks and refinements in various languages, but I think that mostly covers the approaches in use out there. (If you&rsquo;re aware of any others, do tell.) Which approach is the right one for <em>my</em> little scripting language?</p> <p>A deeply related question is the choice of <em>defaults</em>. Java has <code>public</code> for public stuff and <code>private</code> for private stuff, but it also has a syntax for package private declarations: nothing at all. If you don&rsquo;t specify anything, C++ makes your struct members public and your class members private.</p> <p>It&rsquo;s hard to beat &ldquo;zero syntax at all&rdquo; when it comes to brevity, so the choice of what access control a declaration gets by default is an important one.</p> <p>When I put on my rigorous software engineer hat&mdash;probably some kind of construction site hard hat&mdash;the obvious right default is private. No one can fit an entire huge program in their head, so code needs to be broken into smaller isolated pieces you can reason about locally. Defaulting to private encourages users to make smaller independent modules.</p> <p>If I was making a language for big mission-critical infrastructure software, I would definitely do that. I think Rust made the right choice by defaulting to private and requiring you to opt in to public with <code>pub</code>.</p> <p>But I am making, like, the opposite of that. A hobby project that may never see the light of day for people to make their own hobby games that, realistically, also may never see the light of day. If anything, I want to encourage (hypothetical) users of my (currently mostly vaporware) language to <em>get things done</em>. Access control can get in the way of that.</p> <h3 id="class-modifiers-in-dart"><a href="#class-modifiers-in-dart">Class modifiers in Dart<span class="anchor">#class-modifiers-in-dart</span></a></h3> <p>A few years ago, we added a slew of <a href="https://dart.dev/language/class-modifiers">class modifiers</a> to Dart. These are not directly tied to public and private, but in the process of designing those, I spent a lot of time talking to users about how they use the language and what defaults they prefer.</p> <p>Dart defaults to public. You have to opt in to private by prefixing a name with an underscore. It turns out that most users actually do prefer public being the default. And after looking at mountains of Dart code, what I see is that most Dart code is relatively small applications. Those apps are built on top of a set of frameworks and libraries, but compared to the volume of application code out there, those libraries make up a fairly small fraction of the entire Dart ecosystem.</p> <p>These apps are the leaves of the dependency tree. Nothing depends on them or imports them. The authors of most of those are just trying to slap some UI together and ship a thing. And for most of those, defaulting to public keeps the language out of their way.</p> <h2 id="syntax-for-privacy"><a href="#syntax-for-privacy">Syntax for privacy<span class="anchor">#syntax-for-privacy</span></a></h2> <p>That all leads me to feel that my scripting language should default to public and have a way to opt in to private. How do to that?</p> <h3 id="no-export-manifests"><a href="#no-export-manifests">No export manifests<span class="anchor">#no-export-manifests</span></a></h3> <p>I know I don&rsquo;t want export manifests. They are much too verbose. Also, there is some extra language complexity and error reporting that gets sucked in. Since the manifest is separate from the declaration, the compiler has to handle cases where you try to export a name that doesn&rsquo;t exist. My language also has overloading, which means when exporting a function, you would need to a way to specify which of the overloads to export and which to leave private.</p> <p>Export manifests are just way more machinery than fits for a scripting language. This approach is out.</p> <h3 id="no-modifier-sections"><a href="#no-modifier-sections">No modifier sections<span class="anchor">#no-modifier-sections</span></a></h3> <p>The C++ approach is nice in that it&rsquo;s clear while still being pretty terse. You only need to write a modifier and can apply it to a bunch of declarations in the same scope.</p> <p>But at least in C++ (not sure about Ada), that only works within a type declaration. My language isn&rsquo;t really object oriented. It&rsquo;s more procedural. Sort of &ldquo;structs plus functions&rdquo; like C. Instead of classes, you have records. But a record declaration tends to be pretty small with only a handful of fields in it. There&rsquo;s not enough <em>stuff</em> inside a single record to benefit from reusing a modifier for a bunch of nested declarations.</p> <p>I could allow <code>private</code> at the top level and have it apply to everything after, but that feels like it errs too far in the other direction. It would be really confusing to have a thousand line file and not realize that half of it is private because some modifier way offscreen above flipped the access control.</p> <p>Modifier sections are out.</p> <h3 id="no-sigils-in-identifiers"><a href="#no-sigils-in-identifiers">No sigils in identifiers<span class="anchor">#no-sigils-in-identifiers</span></a></h3> <p>Sigils in identifiers do have a sort of terse, scripty feel. No extra keywords.</p> <p>I can&rsquo;t do Go&rsquo;s approach because my language already makes identifier leading case significant. It supports <a href="https://en.wikipedia.org/wiki/Pattern_matching#Tree_patterns">destructuring pattern matching</a>. Like some functional languages, it uses case to distinguish when a pattern is binding a new local variable (lowercase) versus matching against a type or constant (uppercase).</p> <p>Now I&rsquo;m not sure that corner of the language will stick, but it&rsquo;s there right now.</p> <p>I could do Python/Dart&rsquo;s approach and use a leading underscore. But I&rsquo;d really prefer to not have to mention the sigil at every single use site. After having written probably a million lines of Dart code, I know it&rsquo;s not intolerable. But it feels like an annoying tax. And for a language I&rsquo;m designing for my own joy, I&rsquo;d like to eliminate as many annoyances as possible.</p> <h3 id="maybe-modifiers"><a href="#maybe-modifiers">Maybe modifiers<span class="anchor">#maybe-modifiers</span></a></h3> <p>That leaves two approaches and is where I&rsquo;m currently at. Approach one is the typical solution to use an access control modifier at the declaration. Unfortunately, defaulting to public makes this harder. In Rust, <code>pub</code> is a really nice little keyword to flip to public when the default is private, but there is no obvious converse. <code>private</code> would be the longest keyword in my language. <code>pri</code> doesn&rsquo;t read like anything. <code>priv</code> is&hellip; strange. I suppose that <code>pvt</code> is the well-established abbreviation for &ldquo;private&rdquo; but that feels a tad militaristic.</p> <p>I can&rsquo;t find any other synonyms for &ldquo;private&rdquo; that admit reasonable abbreviations either. So I&rsquo;m open to taking this path, but I&rsquo;m simply failing to come up with a good keyword for it.</p> <p>(A silly goblin part of my brain suggested I use <code>shh</code> to mean &ldquo;private&rdquo;, like the declaration is being whispered and can&rsquo;t be heard outside of its module.)</p> <h3 id="maybe-sigils-at-the-declaration"><a href="#maybe-sigils-at-the-declaration">Maybe sigils at the declaration<span class="anchor">#maybe-sigils-at-the-declaration</span></a></h3> <p>This leaves Oberon&rsquo;s weird approach. There are several advantages here:</p> <ul> <li> <p>It is maximally terse. Zero syntax at the use site, and only a single character at the declaration site.</p> </li> <li> <p>No additional reserved words which might get in the way of user identifiers.</p> </li> <li> <p>By defaulting to public, the opacity of the syntax is less of an issue. Yes, the sigil won&rsquo;t immediately convey what it does, but if you&rsquo;re just starting to use the language, you don&rsquo;t need to even know it exists at first. You can just make everything public.</p> </li> </ul> <p>So there is some appeal. I don&rsquo;t think I can literally take Oberon&rsquo;s syntax with <code>*</code> following the name. My language lets you define operators and it would be confusing to have <code>*</code> right after other punctuation:</p> <pre class="highlight language-vgs"><span class="k">def</span> <span class="o">+*</span><span class="p">(</span><span class="i">left</span> <span class="t">Vec</span><span class="p">,</span> <span class="i">right</span> <span class="t">Vec</span><span class="p">)</span> <span class="t">Vec</span><span class="p">(</span><span class="i">left</span><span class="p">.</span><span class="i">x</span> <span class="o">+</span> <span class="i">right</span><span class="p">.</span><span class="i">x</span><span class="p">,</span> <span class="i">left</span><span class="p">.</span><span class="i">y</span> <span class="o">+</span> <span class="i">right</span><span class="p">.</span><span class="i">y</span><span class="p">)</span> <span class="k">end</span></pre> <p>This is declaring a private <code>+</code> function. It would probably look equally weird to have <code>*</code> after the name in a record declaration:</p> <pre class="highlight language-vgs"><span class="k">rec</span> <span class="t">Vec</span><span class="o">*</span> <span class="k">val</span> <span class="i">left</span> <span class="t">Int</span> <span class="k">val</span> <span class="i">right</span> <span class="t">Int</span> <span class="k">end</span></pre> <p>That led me to thinking the sigil should be by the declaration keyword. In my language, every declaration form does start with a leading keyword (unlike, say, C where function declarations don&rsquo;t have one), so it&rsquo;s feasible. Something like:</p> <pre class="highlight language-vgs"><span class="k">rec</span><span class="o">*</span> <span class="t">Vec</span> <span class="k">val</span> <span class="i">left</span> <span class="t">Int</span> <span class="k">val</span> <span class="i">right</span> <span class="t">Int</span> <span class="k">end</span> <span class="k">def</span><span class="o">*</span> <span class="i">sayHi</span><span class="p">()</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;Hello!&quot;</span><span class="p">)</span> <span class="k">end</span></pre> <p>I don&rsquo;t hate it. But using <code>*</code> feels weird for &ldquo;private&rdquo;. If anything, it seems to emphasize the declaration (which is what it does in Oberon where it means &ldquo;public&rdquo;).</p> <h3 id="maybe-alternate-keywords"><a href="#maybe-alternate-keywords">Maybe alternate keywords<span class="anchor">#maybe-alternate-keywords</span></a></h3> <p>That led to my last idea which I can&rsquo;t decide if I like or not. Given Python and Dart, it seems like underscore vaguely conveys &ldquo;private&rdquo; to some people. So maybe use that? It looks like:</p> <pre class="highlight language-vgs"><span class="k">rec_</span> <span class="t">Vec</span> <span class="k">val</span> <span class="i">left</span> <span class="t">Int</span> <span class="k">val</span> <span class="i">right</span> <span class="t">Int</span> <span class="k">end</span> <span class="k">def_</span> <span class="i">sayHi</span><span class="p">()</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;Hello!&quot;</span><span class="p">)</span> <span class="k">end</span></pre> <p>It looks odd, but I don&rsquo;t <em>hate</em> it. Or at least I don&rsquo;t hate it any more than any novel programming language syntax usually triggers revulsion.</p> <p>However, there&rsquo;s sort of a problem. Underscore is a valid identifier character. So the <a href="https://craftinginterpreters.com/scanning.html">lexer</a> is not going to scan <code>rec_</code> as a <code>rec</code> keyword token followed by a <code>_</code> token. Instead of will treat <code>rec_</code> as a single identifier.</p> <p>But then&hellip; I could just let it do that. I&rsquo;d then define a separate set of reserved words for all of the private declaration keywords: <code>def_</code>, <code>rec_</code>, <code>var_</code>, and <code>val_</code>.</p> <p>So now instead of a modifier or sigil to control access, you explicitly choose one of two declaration keywords. One makes it public and one makes it private. This technically means adding a handful more reserved words, but they aren&rsquo;t ones that are particularly useful for users anyway.</p> <p>I keep trying to talk myself out of this approach because it&rsquo;s so unusual but so far it seems to be lodged in my head better than any of the alternatives I&rsquo;ve considered. What do you think?</p> Does Go Have Subtyping? http://journal.stuffwithstuff.com/2023/10/19/does-go-have-subtyping/ Thu, 19 Oct 2023 07:00:00 GMT [email protected] (Robert Nystrom) http://journal.stuffwithstuff.com/2023/10/19/does-go-have-subtyping <p>I&rsquo;ve been <a href="/2023/01/03/type-checking-if-expressions/">noodling on a static type system</a> for my current hobby language. To try to keep the language as simple as possible, I&rsquo;m trying to see if I can live without subtyping. Since most of my programming experience is in object-oriented languages, I&rsquo;ve been learning more about languages that lack&mdash;or at least claim to lack&mdash;subyping, to see how they work.</p> <p>The most intriguing one to me is Go because the authors say it doesn&rsquo;t have subtyping, but when you look at interfaces, it does seem to have something <em>really close</em> to subtyping. Is it subtyping just under another name, or is there really something different going on?</p> <p>This post is the answer to that question as best as I can tell. The short answer is that no, Go doesn&rsquo;t have subtyping. But also, yes, it sort of does.</p> <h2 id="what-is-subtyping"><a href="#what-is-subtyping">What is subtyping?<span class="anchor">#what-is-subtyping</span></a></h2> <p>If you&rsquo;re reading my blog, you probably already know what subtyping is, but let&rsquo;s make sure we&rsquo;re all starting from the same place. Subtyping defines a <a href="https://en.wikipedia.org/wiki/Relation_(mathematics)"><em>relation</em></a> between two types. Given two types A and B, it might be the case that B is a subtype of A, or it might not be.</p> <p>Since subtyping is a relation between a pair of types, it only comes into play in places in code where two types are involved. The main place is assignment. You have an expression of type A and you assign the result to a variable with type B. Is that assignment allowed?</p> <p>Programming language folks usually generalize &ldquo;assignment&rdquo; to mean any place where a variable is given some value. That includes assignment expressions, but also covers initialized variable declarations and function calls where argument values passed to the function are bound to their corresponding parameters.</p> <p>There are a couple of other places where subtyping comes into play, usually around type inference, but assignment is the main one: You have a context that requires some type B and a value of some type A. What are the types A and B where that code is valid?</p> <p>That question is the heart of what a type checker <em>does</em>. The main user interface of a static system is compile errors, and the most common compile error is &ldquo;I expected a value of <em>this</em> type but you gave me a value of <em>this other type</em>&rdquo;.</p> <h2 id="why-have-subtyping"><a href="#why-have-subtyping">Why have subtyping?<span class="anchor">#why-have-subtyping</span></a></h2> <p>You have a context that expects type B and you give it a value of type A. In languages without subtyping, that&rsquo;s only OK if A and B are the exact same type. In Pascal, if you declare a variable with type <code>integer</code>, the only thing you can initialize it with is a value of type <code>integer</code>.</p> <p>Subtyping exists largely to loosen that restriction&mdash;to allow <em>multiple</em> different types to flow into some context. Why might a language want to permit that?</p> <p>The reason is <em>polymorphism</em>: Subtyping lets you write a piece of code and reuse that same code with a range of different (but related) types. In languages without subtyping, you can often find yourself copy/pasting the same function to work with multiple different input types. (Generics can help, but that&rsquo;s another form of polymorphism that we&rsquo;ll ignore for this post.)</p> <p>In, say, Java, if you define a method that takes an <code>Iterable</code>, then you can pass a <code>List</code> to it, a <code>Stack</code>, etc. You get to amortize the usefulness of that method across all types that implement the <code>Iterable</code> interface. Subtyping is a force multiplier for your code.</p> <p>(Of course, that benefit isn&rsquo;t without significant costs in terms of language complexity, which is why I&rsquo;m hoping to avoid it.)</p> <h2 id="does-go-have-subtyping"><a href="#does-go-have-subtyping">Does Go have subtyping?<span class="anchor">#does-go-have-subtyping</span></a></h2> <p>If you search the (extremely well-written!) <a href="https://go.dev/ref/spec">Go language spec</a> for &ldquo;subtype&rdquo;, you get zero results. So the answer is a clear &ldquo;no&rdquo; at the textual level. However, we needer a deeper hermeneutics.</p> <p>Java does have subtyping. Now, if you were to make a new language named &ldquo;Blava&rdquo; that was a literal copy/paste of the Java language specification with every use of &ldquo;subtype&rdquo; replaced with &ldquo;blubtype&rdquo;, would you say that Blava has subtyping? It behaves indistinguishably from a language with subtyping, so I&rsquo;d be inclined to say yes.</p> <p>The Go spec doesn&rsquo;t mention &ldquo;subtype&rdquo;, but it does have a notion of <a href="https://go.dev/ref/spec#Assignability">&ldquo;assignability&rdquo;</a>. When you have a context that expects some type and you give it a value of some other type, assignability determines which set of other types are allowed. Concretely, the rules are:</p> <blockquote> <ul> <li> <p>A non-interface type T is assignable to an interface type I if T implements I.</p> </li> <li> <p>An interface type A is assignable to interface type B if A&rsquo;s methods are a superset of B&rsquo;s.</p> </li> </ul> </blockquote> <p>You know, that sounds an <em>awful lot</em> like subtyping. Is &ldquo;assignable to&rdquo; just Rob Pike&rsquo;s idiosyncratic way of saying &ldquo;subtype of&rdquo;? Does Go have subtyping in everything except name? Are we just playing semantics? (I mean, we&rsquo;re designing a programming language, so obviously <em>everything</em> we do is playing semantics. But I mean are we playing semantics with the language spec itself?) To fully answer that, we&rsquo;ll need to look at all of the kinds of types in a program.</p> <h2 id="composite-types-and-variance"><a href="#composite-types-and-variance">Composite types and variance<span class="anchor">#composite-types-and-variance</span></a></h2> <p>If the only types in Go&rsquo;s type system were primitives like numbers, structs, and interfaces then I think you&rsquo;d have a good argument that Go does have subtyping, just spelled differently. But once you start looking at slice types and function types, the story changes. (And array and channel types too, but slices and functions are enough to make the point.)</p> <p>The thing that these latter kinds of types have in common is that they <em>contain other types</em>. A slice type has an inner type for the slice elements. A function type has a list of parameter types and a list of return types.</p> <p>You ready for some more computer science jargon? We&rsquo;ve been talking about relations on pairs of types like &ldquo;is subtype&rdquo; and &ldquo;is assignable&rdquo;. But now we have types that contain other types. That raises the question of whether a relation on the inner types of two composite types says anything about the relation between the two outer types.</p> <p>For example, let&rsquo;s say we have two slice types <code>[]E1</code> and <code>[]E2</code>. They have element types <code>E1</code> and <code>E2</code>, respectively. If <code>E1</code> is assignable to <code>E2</code> does that mean that <code>[]E1</code> is assignable to <code>[]E2</code>? Does the assignability &ldquo;propagate&rdquo; from the inner types to the outer types?</p> <p>Computer scientists call this property (meta-property?) <em>variance</em>. They phrase the question like &ldquo;how does assignability of slice types <em>vary with respect to their element types</em>?&rdquo;. There are a few possible answers to a question like this.</p> <h3 id="variance-of-slice-types"><a href="#variance-of-slice-types">Variance of slice types<span class="anchor">#variance-of-slice-types</span></a></h3> <p>For slice types in Go specifically, there are a handful of assignability rules, but the only one that applies to slice types is:</p> <blockquote> <ul> <li><p>V and T are identical.</p></li> </ul> </blockquote> <p>In other words, for two slice types to be assignable, they have to be the exact same type. That in turn means they must have the exact same element types. Even if two element types are assignable, slices of those two types are not.</p> <p>Judging by an endless series of confused people asking questions on StackOverflow, that behavior is unintuitive to programmers, both in Go and in other languages. Let&rsquo;s say you have this Go program:</p> <pre class="highlight language-go"><span class="k">type</span> <span class="i">Dog</span> <span class="k">struct</span> <span class="p">{</span> <span class="i">name</span> <span class="t">string</span> <span class="p">}</span> <span class="k">type</span> <span class="i">Barker</span> <span class="k">interface</span> <span class="p">{</span> <span class="i">Bark</span><span class="p">()</span> <span class="p">}</span> <span class="k">func</span> <span class="p">(</span><span class="i">d</span> <span class="i">Dog</span><span class="p">)</span> <span class="i">Bark</span><span class="p">()</span> <span class="p">{</span> <span class="i">fmt</span><span class="p">.</span><span class="i">Println</span><span class="p">(</span><span class="s">&quot;Woof!&quot;</span><span class="p">)</span> <span class="p">}</span></pre> <p>Here we have a <code>Dog</code> concrete type, which is assignable to the interface <code>Barker</code>. So this is fine:</p> <pre class="highlight language-go"><span class="k">func</span> <span class="i">speak</span><span class="p">(</span><span class="i">barker</span> <span class="i">Barker</span><span class="p">)</span> <span class="p">{</span> <span class="i">barker</span><span class="p">.</span><span class="i">Bark</span><span class="p">()</span> <span class="p">}</span> <span class="k">func</span> <span class="i">main</span><span class="p">()</span> <span class="p">{</span> <span class="i">speak</span><span class="p">(</span><span class="i">Dog</span><span class="p">{</span><span class="s">&quot;Sparky&quot;</span><span class="p">})</span> <span class="p">}</span></pre> <p>Given that, you might expect this to work too:</p> <pre class="highlight language-go"><span class="k">func</span> <span class="i">speakAll</span><span class="p">(</span><span class="i">barkers</span> <span class="p">[]</span><span class="i">Barker</span><span class="p">)</span> <span class="p">{</span> <span class="k">for</span> <span class="i">_</span><span class="p">,</span> <span class="i">barker</span> <span class="p">:</span><span class="o">=</span> <span class="k">range</span> <span class="i">barkers</span> <span class="p">{</span> <span class="i">barker</span><span class="p">.</span><span class="i">Bark</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="k">func</span> <span class="i">main</span><span class="p">()</span> <span class="p">{</span> <span class="i">dogs</span> <span class="p">:</span><span class="o">=</span> <span class="p">[]</span><span class="i">Dog</span><span class="p">{</span><span class="i">Dog</span><span class="p">{</span><span class="s">&quot;Sparky&quot;</span><span class="p">},</span> <span class="i">Dog</span><span class="p">{</span><span class="s">&quot;Fido&quot;</span><span class="p">}}</span> <span class="i">speakAll</span><span class="p">(</span><span class="i">dogs</span><span class="p">)</span> <span class="p">}</span></pre> <p>But, no. The type system giveth and the type system taketh away:</p> <pre class="highlight language-text">example.go:29:11: cannot use dogs (variable of type []Dog) as []Barker value in argument to speakAll</pre> <p>If the type system didn&rsquo;t yell at you, this program <em>would</em> be fine at runtime. All it does is call <code>Bark()</code> on every element in the array, and both Sparky and Fido do implement that method. So what&rsquo;s the deal? In this case, the program is <em>coincidentally</em> fine because <code>speakAll()</code> is only reading from the slice. But what if we wrote:</p> <pre class="highlight language-go"><span class="k">type</span> <span class="i">Tree</span> <span class="k">struct</span> <span class="p">{</span> <span class="i">species</span> <span class="t">string</span> <span class="p">}</span> <span class="k">func</span> <span class="p">(</span><span class="i">t</span> <span class="i">Tree</span><span class="p">)</span> <span class="i">Bark</span><span class="p">()</span> <span class="p">{</span> <span class="i">fmt</span><span class="p">.</span><span class="i">Println</span><span class="p">(</span><span class="s">&quot;Rough (but not ruff)!&quot;</span><span class="p">)</span> <span class="p">}</span> <span class="k">func</span> <span class="i">appendTree</span><span class="p">(</span><span class="i">barkers</span> <span class="p">[]</span><span class="i">Barker</span><span class="p">)</span> <span class="p">[]</span><span class="i">Barker</span> <span class="p">{</span> <span class="k">return</span> <span class="i">append</span><span class="p">(</span><span class="i">barkers</span><span class="p">,</span> <span class="i">Tree</span><span class="p">{</span><span class="s">&quot;Elm&quot;</span><span class="p">})</span> <span class="p">}</span></pre> <p>There&rsquo;s nothing wrong with this <code>appendTree()</code> function. It adds a <code>Tree</code> to the given slice. Since <code>Tree</code> is assignable to <code>Barker</code>, that&rsquo;s fine. But if you were to call this and pass in a <code>[]Dog</code>, you&rsquo;d end up with an array of dogs that had a tree stuck in it! That would violate the soundness of the language.</p> <p>This is why Go only treats two slice types as assignable if they have the exact same element types. In PL parlance, slice types are <em>invariant</em> with respect to their element types. And, for a mutable data structure like slices, that rule makes sense.</p> <p>(A reasonable person might wonder then why Java and C# <em>don&rsquo;t</em> have this rule and instead say that array types <em>are</em> assignable if their element types are. And then, because as you can see, it isn&rsquo;t safe to do so, they have to add <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/ArrayStoreException.html">runtime</a> <a href="https://learn.microsoft.com/en-us/dotnet/api/system.arraytypemismatchexception?view=net-7.0">checks</a> if you try to stuff an element of the wrong type into the array.)</p> <p>So, OK, it makes sense for slice (and array) types to be invariant. What about function types?</p> <h3 id="variance-of-function-types"><a href="#variance-of-function-types">Variance of function types<span class="anchor">#variance-of-function-types</span></a></h3> <p>To keep things simple, first we&rsquo;ll consider just functions that don&rsquo;t take any parameters and have a single return type. Given two function types like that, when are they assignable? Again, the only rule in the Go language spec that matches function types is <code>V and T are identical</code>. So two function types are only assignable if they have the exact same return types. Even if the return types are themselves assignable, if they are different types, the functions aren&rsquo;t assignable.</p> <p>Do we need to be that strict to preserve soundness? Actually, no! Here&rsquo;s an example:</p> <pre class="highlight language-go"> <span class="k">func</span> <span class="i">returnDog</span><span class="p">()</span> <span class="i">Dog</span> <span class="p">{</span> <span class="k">return</span> <span class="i">Dog</span><span class="p">{</span><span class="s">&quot;Rex&quot;</span><span class="p">}</span> <span class="p">}</span> <span class="k">func</span> <span class="i">useCallback</span><span class="p">(</span><span class="i">callback</span> <span class="k">func</span><span class="p">()</span> <span class="i">Barker</span><span class="p">)</span> <span class="p">{</span> <span class="i">barker</span> <span class="p">:</span><span class="o">=</span> <span class="i">callback</span><span class="p">()</span> <span class="i">barker</span><span class="p">.</span><span class="i">Bark</span><span class="p">()</span> <span class="p">}</span> <span class="k">func</span> <span class="i">main</span><span class="p">()</span> <span class="p">{</span> <span class="i">useCallback</span><span class="p">(</span><span class="i">returnDog</span><span class="p">)</span> <span class="p">}</span></pre> <p>So we have a function, <code>returnDog</code> that returns a value of type <code>Dog</code>. We pass a reference to that function to <code>useCallback()</code> whic expects a function that returns a <code>Barker</code>. The <code>Dog</code> type does implement <code>Barker</code>. If this program were run, it would be perfectly safe. And, in fact, there&rsquo;s nothing you could put inside <code>useCallback()</code> that would make passing <code>returnDog</code> to it violate the soundness of the type system.</p> <p>It&rsquo;s sound and semantically kosher in principle&hellip; but Go disallows it:</p> <pre class="highlight language-text">.&#47;prog.go:49:14: cannot use returnDog (value of type func() Dog) as func() Barker value in argument to useCallback</pre> <p>Every other language I know that has subtyping and function types allows this. A function type <code>A</code> is a subtype of another function type <code>B</code> if the return type of <code>A</code> is a subtype of the return type of <code>B</code>. So the subtyping relation of the return types propagates out to determine the subtype relation of the function types. We call this <em>covariance</em> and say that function types are <em>covariant in their return types</em>. The &ldquo;co-&rdquo; prefix means that the subtyping relation between the inner types goes in the &ldquo;same direction&rdquo; as the subtyping relation it implies about the outer types.</p> <p>That direction matters because relations like subtyping and assignability aren&rsquo;t symmetric. The <code>Dog</code> type is assignable to <code>Barker</code>, but <code>Barker</code> is <em>not</em> assignable to <code>Dog</code>. The underlying value might be a <code>Tree</code>!</p> <p>Are there cases where the variance of an inner type doesn&rsquo;t go in the same direction as the outer types? Indeed there are, and they&rsquo;re right there next to us. Instead of return types, let&rsquo;s look at parameter types. Now let&rsquo;s say we only care about functions that accept a single parameter and return nothing. Here&rsquo;s an example:</p> <pre class="highlight language-go"><span class="k">func</span> <span class="i">acceptBarker</span><span class="p">(</span><span class="i">barker</span> <span class="i">Barker</span><span class="p">)</span> <span class="p">{</span> <span class="i">barker</span><span class="p">.</span><span class="i">Bark</span><span class="p">()</span> <span class="p">}</span> <span class="k">func</span> <span class="i">useCallback</span><span class="p">(</span><span class="i">callback</span> <span class="k">func</span><span class="p">(</span><span class="i">Dog</span><span class="p">))</span> <span class="p">{</span> <span class="i">callback</span><span class="p">(</span><span class="i">Dog</span><span class="p">{</span><span class="s">&quot;Laika&quot;</span><span class="p">})</span> <span class="p">}</span> <span class="k">func</span> <span class="i">main</span><span class="p">()</span> <span class="p">{</span> <span class="i">useCallback</span><span class="p">(</span><span class="i">acceptBarker</span><span class="p">)</span> <span class="p">}</span></pre> <p>Note that the parameter types are flipped compared to the return type example. Here, the callback type in <code>useCallback()</code> takes a more precise type of <code>Dog</code>. The function we pass to it, <code>acceptBarker</code> has a parameter whose type is <code>Barker</code>.</p> <p>You may feel a slight disorientation here. The code feels weird and sort of backwards. Wait a minute and the dizziness will pass. Dramamine might help.</p> <p>While this definitely isn&rsquo;t as intuitive as return types being covariant, if you think about it carefully, you&rsquo;ll see that the above program is completely sound. In other languages with subtyping, function type <code>A</code> is a subtype of function type <code>B</code> if the parameter types of <code>B</code> are subtypes of the parameter types of <code>A</code>. Note how <code>A</code> and <code>B</code> are reversed in the second half of that sentence. The variance of parameter types is reversed. In technical terms, we say that function types are <em>contravariant in their parameter types</em>. The prefix &ldquo;contra-&rdquo; means &ldquo;against&rdquo;.</p> <p>(You might wonder what happens when you have a function type with a parameter whose type is itself a function type with some parameter type. How does <em>that</em> flow out? When there&rsquo;s two levels of nesting it flips around to going in the same direction as the outermost type. The way I think about it is that contravariance is a 180° flip in the direction of the relation. If you nest contravariant types, you flip it twice and get back to the original direction.)</p> <p>Contravariant parameter types are sound, but again Go doesn&rsquo;t allow them. Two function types are only assignable if their parameter types are <em>exactly the same</em>.</p> <h2 id="invariance-in-go"><a href="#invariance-in-go">Invariance in Go<span class="anchor">#invariance-in-go</span></a></h2> <p>In every language I know with subtyping, function types are <a href="https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Function_types">covariant in their return types and contravariant in their parameter types</a>. But in Go, function types are invariant.</p> <p>Go is not a language known for getting in the programmer&rsquo;s way when they want to do something, so why are function types more restrictive than would be necessary for soundness?</p> <p>It&rsquo;s not just function types either. <em>All</em> composite types are invariant in Go: arrays, slices, channels, maps, functions. So <em>ground</em> types&mdash;types that don&rsquo;t contain any other type&mdash;have some subtype-like notion of assignability. But once you wrap a type in another, any notion of assignability goes away.</p> <p>Why did the designers of Go do that? If you&rsquo;re going to bother having interfaces and assignability, why not go all the way and have assignability for functions and other composite types where its sound?</p> <p>If all the designers cared about was semantic correctness and having a beautiful elegant specification written in LaTeX, then they probably would have supported variance, at least for functions. (The other types all <em>should</em> be invariant since they are mutable. When types can flow both in and out, any other variance isn&rsquo;t sound.)</p> <p>But Go was designed from day one to be a high-performance systems language. It&rsquo;s the exact opposite of an ivory tower language designed for proofs and publications. The goal of the language is to let real users ship real applications. And, importantly, be able to ship <em>fast</em> applications and <em>reason about the performance of their code</em>.</p> <h2 id="representing-values"><a href="#representing-values">Representing values<span class="anchor">#representing-values</span></a></h2> <p>Up to this point, we&rsquo;ve only been concerned with how types flow through the type checker at compile time. But&mdash;assuming there are no compile errors&mdash;the compiler eventually excretes some machine code which gets executed at runtime. When that happens, all of the types the type checker poked at have cracked out of their chrysalides and emerged as beautiful runtime value butterflies flitting around in memory.</p> <p>The choice of how values of different types are represented in memory has a massive effect on performance. So how do the rules around assignability and subtyping interact with those representation choices?</p> <p>In many object-oriented languages (Java, C#, Python, etc.) values of object types are represented by pointers to a heap-allocated structure. That structure has some header information for garbage collection and runtime type tracking, maybe some kind of pointer to a <a href="https://en.wikipedia.org/wiki/Virtual_method_table">vtable</a> for virtual method dispatch, then (finally!) the memory used to store the instance&rsquo;s fields.</p> <p>There are differences between language implementations, of course, but objects are generally both:</p> <ol> <li> <p>Slow to create since they are allocated on the heap.</p> </li> <li> <p>Fairly large with some additional bookkeeping information stored for every single object.</p> </li> <li> <p>Indirect, where a variable or field whose type is an object holds only a pointer to that object, which is always on the heap. Accessing state on an object always requires a pointer indirection which can be slow due to <a href="https://gameprogrammingpatterns.com/data-locality.html">poor locality and cache misses</a>.</p> </li> </ol> <h3 id="struct-types"><a href="#struct-types">Struct types<span class="anchor">#struct-types</span></a></h3> <p>Those are unnacceptable costs for a systems language like Go. When you <em>want</em> runtime polymorphism, of course, you have to pay for it somehow. But if you&rsquo;re just storing data in memory, Go doesn&rsquo;t want to make you pay for something you aren&rsquo;t using. To that end values of struct types in Go store just the bytes needed for the struct&rsquo;s own fields.</p> <p>If a field of a struct is itself some struct type, the inner struct&rsquo;s fields are splatted directly into the surrounding struct&rsquo;s contiguous memory. If you have a local variable of a struct type, the fields are stored right on the stack (unless you take a pointer to the struct which escapes the function).</p> <p>This reduces memory overhead for structs and (probably more importantly for performance) reduces pointer indirections. In a typical Java program, the heap ends up being a huge spiderweb of tiny objects all pointing to each other and the poor CPU an exhausted spider traipsing all over that web trying to find the actual bits of data it wants to eat.</p> <p>In a typical Go program, more state is stored directly on the stack, and the heap is &ldquo;chunkier&rdquo; with fewer, larger blobs of memory. The CPU does fewer hops around the heap and chews on bigger data insects every time it does. That makes memory access more cache friendly and also lightens the load on the garbage collector since there are fewer individual allocations to traverse.</p> <p>(Java does something similar for primitive types, as does C# for struct types.)</p> <h3 id="interface-types"><a href="#interface-types">Interface types<span class="anchor">#interface-types</span></a></h3> <p>So structs are fast, great. But Go does feature runtime polymorphism in the form of interfaces. How does interface method dispatch work if a value is stored directly inline with no extra data to track its runtime type or method implementations?</p> <p>The answer is that interfaces have a <a href="https://research.swtch.com/interfaces">completely different runtime representation</a>. A variable of interface type takes up two words:</p> <ol> <li> <p>A pointer to the type information used for runtime dispatch of the interface methods (in other words, basically a <a href="https://en.wikipedia.org/wiki/Virtual_method_table">vtable</a>).</p> </li> <li> <p>A pointer to the actual data used by the concrete type implementing the interface. (In cases where the data is just a single word, I think it&rsquo;s stored inline.)</p> </li> </ol> <p>The cute industry term for a representation like this is <em>fat pointer</em>: instead of a single word with a single pointer, it&rsquo;s a pair of them, one for data and one for some kind of metadata or bookkeeping information.</p> <p>One of the really cool things about Go is that you only use this representation&mdash;you only pay for the increased memory and indirection cost of this representation&mdash;when you ask for it and when you need it. In places where you need some virtual dispatch, you use an interface type and accept the overhead of a fat pointer and indirection. But in places where you just want to store a single concrete type, you use its underlying type and the memory is stored directly inline.</p> <p>C# supports a similar distinction with classes and structs. But that&rsquo;s mostly a &ldquo;declaration time&rdquo; choice. Once you&rsquo;ve decided something is a class, every variable of that class&rsquo;s type will store it as a reference to a heap-allocated object. Conversely, if you&rsquo;ve declared something as a struct, it will always be stored inline on the stack or in the containing object (unless you go out of your way to box it).</p> <p>In Go, the distinction between stored inline versus stored indirectly is made at each use site. That leads to some additional complexity for the user: they always have to think &ldquo;should I use an interface, pointer, or struct type here?&rdquo;, but it gives them more fine-grained control over how they spend memory and pointer indirection costs.</p> <h2 id="implicit-conversions"><a href="#implicit-conversions">Implicit conversions<span class="anchor">#implicit-conversions</span></a></h2> <p>We&rsquo;re close to understanding why Go lets you assign a struct to an interface but not a slice of those same structs to a slice of that same interface.</p> <p>If structs and interfaces have entirely different memory representations, how does assignability work at all? When you do:</p> <pre class="highlight language-go"><span class="k">type</span> <span class="i">Dog</span> <span class="k">struct</span><span class="p">{</span> <span class="i">name</span> <span class="t">string</span> <span class="p">}</span> <span class="k">type</span> <span class="i">Barker</span> <span class="k">interface</span> <span class="p">{</span> <span class="i">Bark</span><span class="p">()</span> <span class="p">}</span> <span class="k">func</span> <span class="i">main</span><span class="p">()</span> <span class="p">{</span> <span class="k">var</span> <span class="i">barker</span> <span class="i">Barker</span> <span class="o">=</span> <span class="i">Dog</span><span class="p">{</span><span class="s">&quot;Rex&quot;</span><span class="p">}</span> <span class="p">}</span></pre> <p>Shouldn&rsquo;t that just mangle memory when it tries to treat the memory representation of a struct as if it were an interface? The answer is of course no. When compiling your code, Go knows the type of every variable and every expression. At every assignment, variable declaration, or parameter binding, it reports an error if the value isn&rsquo;t assignable to the destination.</p> <p>When the value is assignable, the compiler also knows whether or not those types are the same. If they&rsquo;re exactly identical, then the assignment can be compiled to a single register move or memory copy. When they are different but still assignable types, the compiler <em>silently inserts code to convert the value type&rsquo;s memory representation to the destination type&rsquo;s representation.</em></p> <p>When you assign a value of a struct type to an interface type, the compiler inserts code to build a fat pointer, wire up its method table pointer to the right interface implementation, move the struct&rsquo;s data onto the heap, etc.</p> <p>Likewise, if you assign one interface type to another, the compiler inserts code to copy the data pointers over but look up the correct method table for the destination interface given the type information of the value&rsquo;s interface type.</p> <p>This right here is the reason that all composite types are invariant in Go. When assigning a single value to a related but different type, the compiler can easily insert fixed-cost code to convert the value&rsquo;s runtime representation to the destination type&rsquo;s. But to convert a slice of some struct type to a slice of an interface type would require an <code>O(n)</code> traversal of the entire slice to convert each element.</p> <p>Function types are even harder. In order to support covariant return types and contravariant parameter types, the compiler would need to insert conversion code <em>somewhere</em>, but there&rsquo;s no right place to put it. Putting it inside the function itself doesn&rsquo;t work because it might be called with a variety of different parameter types and we don&rsquo;t know what to convert it from. Putting it at the callsite before the parameters are passed likewise wouldn&rsquo;t work because we don&rsquo;t know what types every callback might require.</p> <p>There is potentially something clever you could do by supporting multiple entrypoints to functions for each pair of source and destination types, but with multiple parameters you quickly run the risk of exponential code size explosions.</p> <p>This is why languages that do support subtyping and variance almost always have a uniform memory representation for all objects that participate in the subtype hierarchy.</p> <h2 id="does-go-have-subtyping-2"><a href="#does-go-have-subtyping">Does Go have subtyping?<span class="anchor">#does-go-have-subtyping</span></a></h2> <p>If you make it this far, congratulations. This ended up being a much deeper dive than I expected. I learned a lot exploring this corner of the language space and I hope you learned something too.</p> <p>Getting back to the original question, I think we could accurately describe Go&rsquo;s subtyping story in two equivalent ways:</p> <ul> <li> <p><strong>Yes, Go has subtyping, but it has no support for variance and all composite types are invariant.</strong> This is, I think, how someone who is focused only on the abstract semantics of the language would describe it. If you were writing papers about type systems and needed to model Go&rsquo;s you might adopt this perspective. If you didn&rsquo;t care about how Go could be efficiently implemented because you were treating it purely as an abstraction, then this is a good way to look at it and compare it to other languages.</p> <p>The main problem with the looking at the language this way is that it obscures <em>why</em> every composite type is invariant.</p> </li> <li> <p><strong>No, Go doesn&rsquo;t have subtyping, but it does have implicit conversions between some pairs of types.</strong> This is how the designers of Go describe the language. It&rsquo;s the way you&rsquo;d want to look at the language if you were tasked with sitting down and writing a production quality implementation of it. It describes what the language <em>actually does mechanically</em> at compile time and runtime.</p> <p>The challenge I found with this perspective is that it made it harder for me to relate Go&rsquo;s design choices to other more explicitly object-oriented languages. You can look at this entire long article as my process of trying to figure out the first interpretation in terms of this one.</p> </li> </ul> <p>I started digging into this not because I&rsquo;m an active Go user and want to know what&rsquo;s going on under the hood. <a href="https://dart.dev/">My job</a> and hobby is designing programming languages, so I want to know how other languages work to see what good ideas are out there to be harvested.</p> <p>So the question always on my mind at this point is, &ldquo;<em>Why</em> did they design it this way and does that choice make sense in other languages?&rdquo; And for this specific design choice, I think it&rsquo;s pretty cool. You can imagine a language wanting three things:</p> <ul> <li> <p><strong>Non-uniform representation:</strong> Values in memory take up only as much space as they need and avoid pointer indirection when possible to maximize runtime efficiency.</p> </li> <li> <p><strong>Polymorphism:</strong> The ability to reuse code to work with a range of values of different types.</p> </li> <li> <p><strong>Variance:</strong> Sort of the &ldquo;lifted&rdquo; form of polymorphism: The ability to reuse code to work with composite types that contain a range of inner types.</p> </li> </ul> <p>Those are all nice to have features, but it&rsquo;s really hard to get all three at once. Most object-oriented languages sacrifice the first one to get the other two. That gives you flexibility and expressiveness but at a pervasive runtime cost spread throughout the entire program.</p> <p>Some statically-typed languages with simpler type systems like C, Pascal, and SML (ignoring modules, which are a whole other thing) give up polymorphism and variance which can give you more efficient representations at the cost of less code reuse.</p> <p>Languages like C++ and Rust more or less give you all three at the expense of the compiler monomorphizing and generating specialized versions of a function for the different types it gets passed, which makes compilation much slower and can have some runtime costs from all of the extra code sitting around in memory.</p> <p>Go is aiming for a sweet spot where they give you fast compiles, efficient runtime execution, and as much flexibility as they can get away with. It sacrifices variance but keeps polymorphism at the individual value level. That married with implicit conversions enables non-uniform representation. Of the three, variance is probably the least valuable for users, so I think that&rsquo;s a pretty smart trade-off.</p> Representing Heterogeneous Data http://journal.stuffwithstuff.com/2023/08/04/representing-heterogeneous-data/ Fri, 04 Aug 2023 07:00:00 GMT [email protected] (Robert Nystrom) http://journal.stuffwithstuff.com/2023/08/04/representing-heterogeneous-data <p>As I mentioned in the <a href="/2023/01/03/type-checking-if-expressions/">last post</a>, I&rsquo;m working on taking my little videogame scripting language and turning it into a statically typed one. As much as possible, I&rsquo;m trying to make the language simple and familiar. But sometimes those goals are in opposition and the most familiar solution to a problem is kind of a mess.</p> <p>So, I&rsquo;m also exploring novel approaches and delving deeper into programming language history to scavenge forgotten ideas.</p> <h2 id="the-heterogeneous-data-problem"><a href="#the-heterogeneous-data-problem">The heterogeneous data problem<span class="anchor">#the-heterogeneous-data-problem</span></a></h2> <p>One problem every language has to solve is giving users a way to represent <em>heterogeneous data</em>. By that, I mean:</p> <ul> <li> <p><strong>Data that might or might not be present.</strong> Imagine you have a record for storing a street address:</p> <pre class="highlight language-vgs"><span class="k">rec</span> <span class="t">Address</span> <span class="k">var</span> <span class="i">number</span> <span class="t">Int</span> <span class="k">var</span> <span class="i">street</span> <span class="t">String</span> <span class="k">var</span> <span class="i">apartmentNumber</span> <span class="t">Int</span> <span class="k">var</span> <span class="i">city</span> <span class="t">String</span> <span class="k">var</span> <span class="i">zipCode</span> <span class="t">Int</span> <span class="k">var</span> <span class="i">state</span> <span class="t">String</span> <span class="k">end</span></pre> <p>But some addresses don&rsquo;t have apartment numbers. How do you store the apartment number when an address has one but also support its absence?</p> </li> <li> <p><strong>Data that might be in one of several different forms.</strong> You&rsquo;re making a game where a hero can wield weapons. Melee weapons like swords have a single number for how much damage they do. Ranged weapons like crossbows have a pair of numbers for the minimum and maximum range they can reach. How do different kinds of weapons have different fields?</p> </li> </ul> <p>These are two sides of the exact same coin. You can treat optional data as data that can be in one of two forms: present with an associated value or absent with no value attached. Functional languages with an <a href="https://en.wikipedia.org/wiki/Option_type">option or maybe type</a> do exactly that: The language directly supports data that can have one of multiple forms, and they model absent data using that.</p> <p>Conversely, you could model data being in one of several different forms by having separate fields for all possible forms it could be in. At any point in time, only one of the fields has a value and the others are all absent. If you&rsquo;ve ever found yourself building a struct or class and writing a comment that says &ldquo;If this field is blah then this other field will be null.&rdquo; then you&rsquo;ve taken this path (and probably felt a little gross doing it).</p> <h2 id="what-other-languages-do"><a href="#what-other-languages-do">What other languages do<span class="anchor">#what-other-languages-do</span></a></h2> <p>I don&rsquo;t know if broad language tours are your thing, but so much of my job working on <a href="https://dart.dev">Dart</a> involves researching how other languages solve a problem that I can&rsquo;t help myself anymore.</p> <p>There are a handful of solutions to the problem. I&rsquo;ll just throw out the ones I know:</p> <ul> <li> <p><strong>Null.</strong> The <a href="https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/">most famously maligned</a> approach is to allow any reference variable to potentially refer to &ldquo;null&rdquo;, &ldquo;nil&rdquo;, or nothing. This means every reference type can directly also represent an absent value. Of course, the problem is that many data fields <em>aren&rsquo;t</em> heterogeneous and <em>should</em> always be present. If you make every single reference nullable, you&rsquo;ve lost the ability to distinguish ones that can be absent from ones that really shouldn&rsquo;t be.</p> <p>This is why many newer statically typed languages either don&rsquo;t support null at all (Rust and most other statically typed functional languages) or support <a href="https://en.wikipedia.org/wiki/Nullable_type">non-nullable types</a> (Dart, Kotlin, and TypeScript).</p></li> <li> <p><strong>Variant types.</strong> A &ldquo;variant&rdquo; type is a type that can hold a value of one of multiple different types. You can think of nullable references as a limited variant type that can hold either a value of one specific type or the special <code>null</code> value but that&rsquo;s it. Some languages have looser variants that let you store values of any type in the same variable.</p> </li> <li> <p><strong>Untagged unions.</strong> C lets you define a data structure whose fields all share overlapping memory. If you have a few different pieces of data that are <em>disjoint</em>&mdash;you should only ever have one of them and not the others&mdash;then this avoids the memory overhead of storing them all separately.</p> <p>However, in C, the language itself doesn&rsquo;t keep track of <em>which</em> piece of data you have in the union. It will freely let you write one field and then read out another and it will happily just reinterpret the bits in memory as that other type. Efficient, yes. Safe? No.</p> </li> <li> <p><strong>Sum types.</strong> Functional languages going all the way back to <a href="https://en.wikipedia.org/wiki/ML_(programming_language)">ML</a> have a feature also sometimes confusingly called &ldquo;unions&rdquo; that is fairly different. Again, you have an object that can store one of a few different kinds of data. But the language also stores a <em>tag</em> in there so that it knows at runtime which piece of data you have. (This is why they&rsquo;re also called &ldquo;tagged unions&rdquo; or &ldquo;discriminated unions&rdquo;.)</p> <p>The language uses <a href="https://dart.dev/language/patterns#algebraic-data-types">pattern matching</a> to cleverly prevent you from accessing the data as the wrong type.</p></li> <li> <p><strong>Subtyping.</strong> The object-oriented dual to sum types is subtyping: either inheritance or interface implementation. In an object-oriented language, we could model our weapon example like:</p> <pre class="highlight language-dart"><span class="k">interface</span> <span class="k">class</span> <span class="t">Weapon</span> <span class="p">{}</span> <span class="k">class</span> <span class="t">MeleeWeapon</span> <span class="k">implements</span> <span class="t">Weapon</span> <span class="p">{</span> <span class="t">int</span> <span class="i">damage</span><span class="p">;</span> <span class="t">MeleeWeapon</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="i">damage</span><span class="p">);</span> <span class="p">}</span> <span class="k">class</span> <span class="t">RangedWeapon</span> <span class="k">implements</span> <span class="t">Weapon</span> <span class="p">{</span> <span class="t">int</span> <span class="i">minRange</span><span class="p">;</span> <span class="t">int</span> <span class="i">maxRange</span><span class="p">;</span> <span class="t">RangedWeapon</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="i">minRange</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="i">maxRange</span><span class="p">);</span> <span class="p">}</span></pre> <p>Code that wants to work with weapons generally uses the <code>Weapon</code> supertype. The two subtypes for melee and ranged weapons each store the fields they need. If you want to go all the way to an object-oriented style, these fields would be private and then you&rsquo;d have abstract methods in <code>Weapon</code> that are overridden in the subclasses to use them.</p> <p>It&rsquo;s a complex, heavyweight approach, but a powerful and flexible one.</p> </li> </ul> <p>There may be a couple of other weirder language features you can use to model varied data, but I think these are the big ones. Languages tend to pick and choose from this list:</p> <ul> <li> <p><strong>Dynamically typed languages</strong> essentially treat <em>all</em> variables as variant types. And all the ones I know also go ahead and allow null too. If you&rsquo;re not going to have any static checking anyway, you may as well be maximally permissive, I guess.</p> </li> <li> <p><strong>Statically typed functional languages</strong> lean really hard on sum types.</p> </li> <li> <p><strong>Object-oriented languages</strong> obviously primarily use subtyping, though most also have nullable reference types.</p> </li> <li> <p><strong>C</strong> makes pointer types nullable and supports untagged unions. It doesn&rsquo;t have (checked) variants or subtyping, but it can approximate both by allowing pointers to be cast to different types. <strong>C++</strong> takes everything C has and also explicitly supports subtyping.</p> </li> </ul> <p>Newer, bigger multi-paradigm languages like C# and Swift tend to take just about all of the approaches.</p> <h2 id="whither-for-my-little-language"><a href="#whither-for-my-little-language">Whither for my little language?<span class="anchor">#whither-for-my-little-language</span></a></h2> <p>OK, so what&rsquo;s the right approach for my aspirationally simple and elegant statically typed game scripting language?</p> <p>I quite like object-oriented programming in general, but subtyping adds a <em>lot</em> of complexity to a static type system, so my current plan is to not have subtyping in the language at all. That rules out that approach.</p> <p>My goal is for the language to be fairly high level and expressive. It&rsquo;s supposed to be a language that makes making games <em>fun</em>, not necessarily a high-performance machine for engineering giant AAA games. I want you to have a good time tinkering on pixelly 2D games, not write the next Unreal Engine in it. To that end, the language is garbage-collected. That means memory safety, which rules out untagged unions.</p> <p>Back when this language was dynamically typed, it had <code>nil</code>, so that&rsquo;s an obvious approach. But I&rsquo;ve spent, like, <a href="/2011/10/29/a-proposal-for-null-safety-in-dart/">way too much of my time</a> <a href="https://medium.com/dartlang/announcing-dart-3-53f065a10635">rooting out nullable references from Dart</a> and the last thing I want to do with my hobby project is to go back to square one.</p> <p>That basically just leaves sum types and variant types. Given that my language is statically typed and not object-oriented, sum types are the obvious approach. Everyone who uses sum types loves them, myself included. Algebraic datatypes are just <em>cool.</em></p> <p>And, in fact, I went ahead and implemented a protype of sum types and pattern matching and destructuring in my language. It worked. It was&hellip; just OK. To explain why requires a little context</p> <h2 id="an-imperative-procedural-language"><a href="#an-imperative-procedural-language">An imperative, procedural language<span class="anchor">#an-imperative-procedural-language</span></a></h2> <p>My language is unabashedly imperative. I <em>like</em> imperative programming, especially for scripting little games. Games are giant balls of mutable state. I&rsquo;ve watched my kids and many others learn to program, and imperatively modifying stuff seems to be a natural way to think about defining a process.</p> <p>When you read a recipe for cake, you don&rsquo;t see steps like: &ldquo;Produce a new bowl of batter which is the previous bowl of batter and 2 cups of sugar.&rdquo; It just says &ldquo;Add 2 cups of sugar to the bowl.&rdquo;</p> <p>Now, I know all of the problems with mutation of state and imperative code when programming in the large. I get it. But this is supposed to be a fun little language for fun little games and, to me, imperative programming fits that to a tee.</p> <p>The basic vibe I have for the language is similar to Pascal, C, or BASIC: In other words, a classic procedural language. Structures and functions. It looks like this:</p> <pre class="highlight language-vgs"><span class="k">rec</span> <span class="t">MeleeWeapon</span> <span class="k">var</span> <span class="i">damage</span> <span class="t">Int</span> <span class="k">end</span> <span class="k">def</span> <span class="i">attack</span><span class="p">(</span><span class="i">weapon</span> <span class="t">MeleeWeapon</span><span class="p">,</span> <span class="i">monster</span> <span class="t">Monster</span><span class="p">,</span> <span class="i">distance</span> <span class="t">Int</span><span class="p">)</span> <span class="k">if</span> <span class="i">distance</span> <span class="o">&gt;</span> <span class="n">1</span> <span class="k">then</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;You are out of range.&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="k">end</span> <span class="k">var</span> <span class="i">damage</span> <span class="o">=</span> <span class="i">rollDice</span><span class="p">(</span><span class="i">weapon</span><span class="p">.</span><span class="i">damage</span><span class="p">)</span> <span class="k">if</span> <span class="i">monster</span><span class="p">.</span><span class="i">health</span> <span class="o">&lt;=</span> <span class="i">damage</span> <span class="k">then</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;You kill the monster!&quot;</span><span class="p">)</span> <span class="i">monster</span><span class="p">.</span><span class="i">health</span> <span class="o">=</span> <span class="n">0</span> <span class="k">else</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;You wound the monster.&quot;</span><span class="p">)</span> <span class="i">monster</span><span class="p">.</span><span class="i">health</span> <span class="o">=</span> <span class="i">monster</span><span class="p">.</span><span class="i">health</span> <span class="o">-</span> <span class="i">damage</span> <span class="k">end</span> <span class="k">end</span></pre> <p>What&rsquo;s cool about simple procedural code is that even though I have no idea what language you know and you <em>certainly</em> have never programmed in <em>this</em> one, I&rsquo;m still pretty confident that you understand this code.</p> <h2 id="with-sum-types"><a href="#with-sum-types">With sum types<span class="anchor">#with-sum-types</span></a></h2> <p>Let&rsquo;s see how it looks with something like sum types:</p> <pre class="highlight language-vgs"><span class="k">rec</span> <span class="t">Weapon</span> <span class="k">case</span> <span class="t">MeleeWeapon</span> <span class="k">var</span> <span class="i">damage</span> <span class="t">Int</span> <span class="k">case</span> <span class="t">RangedWeapon</span> <span class="k">var</span> <span class="i">minRange</span> <span class="t">Int</span> <span class="k">var</span> <span class="i">maxRange</span> <span class="t">Int</span> <span class="k">end</span> <span class="k">def</span> <span class="i">attack</span><span class="p">(</span><span class="i">weapon</span> <span class="t">Weapon</span><span class="p">,</span> <span class="i">monster</span> <span class="t">Monster</span><span class="p">,</span> <span class="i">distance</span> <span class="t">Int</span><span class="p">)</span> <span class="k">var</span> <span class="i">isInRange</span> <span class="o">=</span> <span class="k">match</span> <span class="i">weapon</span> <span class="k">case</span> <span class="t">MeleeWeapon</span><span class="p">(</span><span class="i">damage</span><span class="p">)</span> <span class="k">then</span> <span class="i">distance</span> <span class="o">==</span> <span class="n">1</span> <span class="k">case</span> <span class="t">RangedWeapon</span><span class="p">(</span><span class="i">min</span><span class="p">,</span> <span class="i">max</span><span class="p">)</span> <span class="k">then</span> <span class="i">distance</span> <span class="o">&gt;=</span> <span class="i">min</span> <span class="k">and</span> <span class="i">distance</span> <span class="o">&lt;=</span> <span class="i">max</span> <span class="k">end</span> <span class="k">if</span> <span class="o">!</span><span class="i">isInRange</span> <span class="k">then</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;You are out of range.&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="k">end</span> <span class="k">var</span> <span class="i">damage</span> <span class="o">=</span> <span class="k">match</span> <span class="i">weapon</span> <span class="k">case</span> <span class="t">MeleeWeapon</span><span class="p">(</span><span class="i">damage</span><span class="p">)</span> <span class="k">then</span> <span class="i">rollDice</span><span class="p">(</span><span class="i">damage</span><span class="p">)</span> <span class="k">case</span> <span class="t">RangedWeapon</span><span class="p">(</span><span class="i">min</span><span class="p">,</span> <span class="i">max</span><span class="p">)</span> <span class="k">then</span> <span class="i">max</span> <span class="o">-</span> <span class="i">min</span> <span class="k">end</span> <span class="k">if</span> <span class="i">monster</span><span class="p">.</span><span class="i">health</span> <span class="o">&lt;=</span> <span class="i">damage</span> <span class="k">then</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;You kill the monster!&quot;</span><span class="p">)</span> <span class="i">monster</span><span class="p">.</span><span class="i">health</span> <span class="o">=</span> <span class="n">0</span> <span class="k">else</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;You wound the monster.&quot;</span><span class="p">)</span> <span class="i">monster</span><span class="p">.</span><span class="i">health</span> <span class="o">=</span> <span class="i">monster</span><span class="p">.</span><span class="i">health</span> <span class="o">-</span> <span class="i">damage</span> <span class="k">end</span> <span class="k">end</span></pre> <p>The sort of weird <code>rec</code> syntax is defining a sum type, <code>Weapon</code>, with type constructors <code>MeleeWeapon</code> and <code>RangedWeapon</code>. I&rsquo;m still noodling on the syntax.</p> <p>Now, the code here works. And it&rsquo;s safe. The compiler and the structure of the pattern matching code itself prevent you from accessing the wrong fields from a weapon of a different kind. That&rsquo;s cool.</p> <p>But it&rsquo;s so much <em>weirder</em> than the previous code. In a procedural language, the idiomatic way to access fields on records is simply <code>record.field</code>. That syntax is in almost every programming language all the way back to Algol. But once you hop over to sum types, you lose that syntax entirely and have to instead sort of &ldquo;invert&rdquo; the code and use pattern matching and destructuring.</p> <p>I do love pattern matching and destructuring&mdash;I just spent the past year of my life <a href="https://github.com/dart-lang/language/blob/main/accepted/3.0/patterns/feature-specification.md">adding it to Dart</a>. But for <em>this</em> language, I&rsquo;m pushing really hard on simplicity. If possible, I don&rsquo;t want <em>two</em> different ways to access state on a value, depending on whether the field is case-specific or not.</p> <p>More to the point, there&rsquo;s no graceful way to handle <em>mutable</em> sum type fields using pattern matching. SML eschews mutability in general and then works around it by allowing you to define explicit mutable ref types. But that&rsquo;s definitely not how my language rolls.</p> <h2 id="variant-records"><a href="#variant-records">Variant records<span class="anchor">#variant-records</span></a></h2> <p>There is <em>one</em> other approach to heterogeneous data that I found that I didn&rsquo;t put in the list up there because, as far as I can tell, it&rsquo;s basically a dead end in the evolutionary history of programming languages.</p> <p>Some versions of Pascal have a thing called &ldquo;variant records&rdquo;. A record in Pascal is your basic &ldquo;collection of fields&rdquo; struct type. A <em>variant</em> record says that <em>some</em> of those fields are only accessible when the record is one of a few different enumerated states.</p> <p>In C, it&rsquo;s common to wrap an untagged union in a struct along with a tag enum indicating which branch of the union is active:</p> <pre class="highlight language-c"><span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span> <span class="r">WEAPON_MELEE</span><span class="p">,</span> <span class="r">WEAPON_RANGED</span> <span class="p">}</span> <span class="t">WeaponType</span><span class="p">;</span> <span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> <span class="t">WeaponType</span> <span class="i">type</span><span class="p">;</span> <span class="k">union</span> <span class="p">{</span> <span class="k">struct</span> <span class="p">{</span> <span class="t">int</span> <span class="i">damage</span><span class="p">;</span> <span class="p">}</span> <span class="i">melee</span><span class="p">;</span> <span class="k">struct</span> <span class="p">{</span> <span class="t">int</span> <span class="i">minRange</span><span class="p">;</span> <span class="t">int</span> <span class="i">maxRange</span><span class="p">;</span> <span class="p">}</span> <span class="i">ranged</span><span class="p">;</span> <span class="p">}</span> <span class="i">as</span><span class="p">;</span> <span class="p">}</span> <span class="t">Weapon</span><span class="p">;</span></pre> <p>Using it looks something like:</p> <pre class="highlight language-c"><span class="t">Weapon</span> <span class="i">weapon</span><span class="p">;</span> <span class="i">weapon</span><span class="p">.</span><span class="i">type</span> <span class="o">=</span> <span class="r">WEAPON_MELEE</span><span class="p">;</span> <span class="i">weapon</span><span class="p">.</span><span class="i">as</span><span class="p">.</span><span class="i">melee</span><span class="p">.</span><span class="i">damage</span> <span class="o">=</span> <span class="n">6</span><span class="p">;</span></pre> <p>A variant record in Pascal (as I understand it from the half dozen ancient slideshows I&rsquo;ve been able to find about it) essentially models that pattern directly.</p> <p>The cool thing about this feature is that the variant-specific fields are accessed using the same familiar field access syntax used everywhere else. That also means variant-specific fields can be mutable.</p> <p>Of course, the <em>not</em> cool thing about using that same field syntax is that there&rsquo;s nothing preventing you from accessing the <em>wrong</em> variant field:</p> <pre class="highlight language-c"><span class="t">Weapon</span> <span class="i">weapon</span><span class="p">;</span> <span class="i">weapon</span><span class="p">.</span><span class="i">type</span> <span class="o">=</span> <span class="r">WEAPON_MELEE</span><span class="p">;</span> <span class="i">weapon</span><span class="p">.</span><span class="i">as</span><span class="p">.</span><span class="i">melee</span><span class="p">.</span><span class="i">damage</span> <span class="o">=</span> <span class="n">6</span><span class="p">;</span> <span class="i">printf</span><span class="p">(</span><span class="s">&quot;Min range %d</span><span class="e">\n</span><span class="s">&quot;</span><span class="p">,</span> <span class="i">weapon</span><span class="p">.</span><span class="i">as</span><span class="p">.</span><span class="i">ranged</span><span class="p">.</span><span class="i">minRange</span><span class="p">);</span> <span class="c">// Oops.</span></pre> <p>There is a type tag, but the language doesn&rsquo;t know and doesn&rsquo;t check it. This is definitely true in C and I think true in Pascal. (It&rsquo;s always hard to talk about Pascal definitively because there&rsquo;s no &ldquo;Pascal&rdquo;, just a huge family of loosely-related Pascal-ish languages.)</p> <p>In a memory safe language like mine, I definitely don&rsquo;t want users to be able to reinterpret memory. But that&rsquo;s a solvable problem.</p> <h2 id="record-cases"><a href="#record-cases">Record cases<span class="anchor">#record-cases</span></a></h2> <p>Which, finally, brings us to the feature I designed for my language. It&rsquo;s very close to variant records in Pascal. The type declaration looks just like the sum type example:</p> <pre class="highlight language-vgs"><span class="k">rec</span> <span class="t">Weapon</span> <span class="k">case</span> <span class="t">MeleeWeapon</span> <span class="k">var</span> <span class="i">damage</span> <span class="t">Int</span> <span class="k">case</span> <span class="t">RangedWeapon</span> <span class="k">var</span> <span class="i">minRange</span> <span class="t">Int</span> <span class="k">var</span> <span class="i">maxRange</span> <span class="t">Int</span> <span class="k">end</span></pre> <p>The difference is that you don&rsquo;t need to rely on pattern matching to access the variant fields. They&rsquo;re just fields:</p> <pre class="highlight language-vgs"><span class="k">def</span> <span class="i">attack</span><span class="p">(</span><span class="i">weapon</span> <span class="t">Weapon</span><span class="p">,</span> <span class="i">monster</span> <span class="t">Monster</span><span class="p">,</span> <span class="i">distance</span> <span class="t">Int</span><span class="p">)</span> <span class="k">if</span> <span class="i">weapon</span> <span class="k">is</span> <span class="t">MeleeWeapon</span> <span class="k">and</span> <span class="i">distance</span> <span class="o">&gt;</span> <span class="n">1</span> <span class="k">or</span> <span class="i">distance</span> <span class="o">&lt;</span> <span class="i">weapon</span><span class="p">.</span><span class="i">minRange</span> <span class="k">or</span> <span class="i">distance</span> <span class="o">&gt;</span> <span class="i">weapon</span><span class="p">.</span><span class="i">maxRange</span> <span class="k">then</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;You are out of range.&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="k">end</span> <span class="k">var</span> <span class="i">damage</span> <span class="o">=</span> <span class="k">if</span> <span class="i">weapon</span> <span class="k">is</span> <span class="t">MeleeWeapon</span> <span class="k">then</span> <span class="i">rollDice</span><span class="p">(</span><span class="i">weapon</span><span class="p">.</span><span class="i">damage</span><span class="p">)</span> <span class="k">else</span> <span class="i">weapon</span><span class="p">.</span><span class="i">maxRange</span> <span class="o">-</span> <span class="i">weapon</span><span class="p">.</span><span class="i">minRange</span> <span class="k">end</span> <span class="k">if</span> <span class="i">monster</span><span class="p">.</span><span class="i">health</span> <span class="o">&lt;=</span> <span class="i">damage</span> <span class="k">then</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;You kill the monster!&quot;</span><span class="p">)</span> <span class="i">monster</span><span class="p">.</span><span class="i">health</span> <span class="o">=</span> <span class="n">0</span> <span class="k">else</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;You wound the monster.&quot;</span><span class="p">)</span> <span class="i">monster</span><span class="p">.</span><span class="i">health</span> <span class="o">=</span> <span class="i">monster</span><span class="p">.</span><span class="i">health</span> <span class="o">-</span> <span class="i">damage</span> <span class="k">end</span> <span class="k">end</span></pre> <p>Of course, you lose the compile-time safety that pattern matching gives you where you can&rsquo;t access fields of the wrong type. But we don&rsquo;t need to go all the way to C&rsquo;s level of unsafety. Instead, when you access a case-specific field on a record, if the record&rsquo;s type tag is set to a different case, the access throws a <em>runtime</em> error. This preserves memory safety.</p> <p>This is a real trade-off. The feature I have here provides strictly less static safety than using sum types. There is a slight performance cost to checking the type tag when accessing case-specific fields. In return, you get simpler, more familiar syntax for working with case-specific fields, including mutable ones.</p> <p>Also, it allows a single record to have a mixture of shared and case-specific fields:</p> <pre class="highlight language-vgs"><span class="k">rec</span> <span class="t">Weapon</span> <span class="k">var</span> <span class="i">name</span> <span class="t">String</span> <span class="k">var</span> <span class="i">bonus</span> <span class="t">Int</span> <span class="k">case</span> <span class="t">MeleeWeapon</span> <span class="k">var</span> <span class="i">damage</span> <span class="t">Int</span> <span class="k">case</span> <span class="t">RangedWeapon</span> <span class="k">var</span> <span class="i">minRange</span> <span class="t">Int</span> <span class="k">var</span> <span class="i">maxRange</span> <span class="t">Int</span> <span class="k">end</span></pre> <p>Here, <code>name</code> and <code>bonus</code> can be accessed on all <code>Weapon</code> instances, but the other fields are case specific. It sort of combines product and sum types into a single construct. I&rsquo;ve found this to be really handy in practice.</p> <p>I haven&rsquo;t decided if I&rsquo;m totally sold on this feature yet. But in the (admittedly small) amount of example code I&rsquo;ve written using it so far, it seems to feel pretty nice. For a small game scripting language, I think it may strike a decent balance between static safety and simplicity.</p> <h2 id="update-what-about-flow-typing"><a href="#update-what-about-flow-typing">Update: What about flow typing?<span class="anchor">#update-what-about-flow-typing</span></a></h2> <p>When I first posted this, the most common reply was why not do some sort of flow typing? In code like:</p> <pre class="highlight language-vgs"><span class="k">def</span> <span class="i">attack</span><span class="p">(</span><span class="i">weapon</span> <span class="t">Weapon</span><span class="p">,</span> <span class="i">monster</span> <span class="t">Monster</span><span class="p">,</span> <span class="i">distance</span> <span class="t">Int</span><span class="p">)</span> <span class="k">if</span> <span class="i">weapon</span> <span class="k">is</span> <span class="t">RangedWeapon</span> <span class="k">and</span> <span class="p">(</span><span class="i">distance</span> <span class="o">&lt;</span> <span class="i">weapon</span><span class="p">.</span><span class="i">minRange</span> <span class="k">or</span> <span class="i">distance</span> <span class="o">&gt;</span> <span class="i">weapon</span><span class="p">.</span><span class="i">maxRange</span><span class="p">)</span> <span class="k">or</span> <span class="i">distance</span> <span class="o">&gt;</span> <span class="n">1</span> <span class="k">then</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;You are out of range.&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="k">end</span> <span class="c"># ...</span> <span class="k">end</span></pre> <p>The compiler could do control flow analysis to determine that the <code>.minRange</code> and <code>.maxRange</code> calls are guarded by an <code>is RangedWeapon</code> and thus allow them. But if you <em>don&rsquo;t</em> guard the code with that kind of check, you&rsquo;d get an error:</p> <pre class="highlight language-vgs"><span class="k">def</span> <span class="i">attack</span><span class="p">(</span><span class="i">weapon</span> <span class="t">Weapon</span><span class="p">,</span> <span class="i">monster</span> <span class="t">Monster</span><span class="p">,</span> <span class="i">distance</span> <span class="t">Int</span><span class="p">)</span> <span class="k">if</span> <span class="i">distance</span> <span class="o">&lt;</span> <span class="i">weapon</span><span class="p">.</span><span class="i">minRange</span> <span class="k">or</span> <span class="c"># Error! Can&#39;t access .minRange here.</span> <span class="i">distance</span> <span class="o">&gt;</span> <span class="i">weapon</span><span class="p">.</span><span class="i">maxRange</span> <span class="k">then</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;You are out of range.&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="k">end</span> <span class="c"># ...</span> <span class="k">end</span></pre> <p>This is definitely a thing you can do! TypeScript, Kotlin, Flow, Dart, and others all support it. The general technique is called &ldquo;control flow analysis&rdquo; and the specific feature is called &ldquo;flow typing&rdquo;, &ldquo;smart casts&rdquo;, or &ldquo;type promotion&rdquo; depending on which language.</p> <p>Is it a good fit for my language? I do like that it makes imperative code &ldquo;just work&rdquo; while being safe. But that &ldquo;just&rdquo; is doing a lot of heavy lifting. We do this analysis in Dart and it is <em>fantastically</em> complex. Proving that a certain piece of code can only be reached by going through some other piece of code first gets hard quickly in the presence of loops and closures. It seems like every release of Dart, we ship more extensions to flow analysis because users keep expecting it to be smarter and smarter.</p> <p>Also, it isn&rsquo;t sound in many cases that users expect to work. Once the variable that you&rsquo;re type testing can escape the current function, the compiler generally can&rsquo;t prove that it won&rsquo;t be mutated between when you test its type and when you use it as the more precise type later.</p> <p>Overall, my feeling is that it works out pretty well for Dart, but it&rsquo;s a large sort of messy feature that feels a little too magical. A goal with my hobby language is that you should be able to have the whole language loaded into your head and rarely be surprised by what it does. Flow analysis in Dart still fairly often surprises me and I <em>literally work on the language full-time</em>.</p> <p>There&rsquo;s also the question of what you promote the tested variable <em>to</em>. In my language as it currently stands, there is no subtyping. <code>MeleeWeapon</code> isn&rsquo;t a subtype of <code>Weapon</code>, it&rsquo;s a case constructor. The <code>weapon is MeleeWeapon</code> syntax looks like a type test, but it&rsquo;s really more like an enum case check.</p> <p>So after that test, what type would <code>weapon</code> have? It would still have to be <code>Weapon</code>. I guess I could make this work by not promoting the <em>type</em> but by having the type checker track an extra &ldquo;known case&rdquo; property for each static type and then use that. That might work. But even with that, I worry that it would quickly become annoying. Let&rsquo;s say you refactor the above code to:</p> <pre class="highlight language-vgs"><span class="k">def</span> <span class="i">attack</span><span class="p">(</span><span class="i">weapon</span> <span class="t">Weapon</span><span class="p">,</span> <span class="i">monster</span> <span class="t">Monster</span><span class="p">,</span> <span class="i">distance</span> <span class="t">Int</span><span class="p">)</span> <span class="k">if</span> <span class="i">weapon</span> <span class="k">is</span> <span class="t">RangedWeapon</span> <span class="k">and</span> <span class="i">checkRange</span><span class="p">(</span><span class="i">weapon</span><span class="p">,</span> <span class="i">distance</span><span class="p">)</span> <span class="k">or</span> <span class="i">distance</span> <span class="o">&gt;</span> <span class="n">1</span> <span class="k">then</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;You are out of range.&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="k">end</span> <span class="c"># ...</span> <span class="k">end</span> <span class="k">def</span> <span class="i">checkRange</span><span class="p">(</span><span class="i">weapon</span> <span class="t">Weapon</span><span class="p">,</span> <span class="i">distance</span> <span class="t">Int</span><span class="p">)</span> <span class="t">Bool</span> <span class="i">distance</span> <span class="o">&lt;</span> <span class="i">weapon</span><span class="p">.</span><span class="i">minRange</span> <span class="k">or</span> <span class="i">distance</span> <span class="o">&gt;</span> <span class="i">weapon</span><span class="p">.</span><span class="i">maxRange</span> <span class="k">end</span></pre> <p>That no longer works. Inside <code>checkRange()</code> the compiler has lost track that <code>weapon</code> is always a <code>RangedWeapon</code>. You could come up with a way to annotate that, but now we&rsquo;re back to subtyping and all the complexity it involves.</p> <p>So, overall, yes, subtyping and flow analysis is a thing that could work here, but I&rsquo;m trying to avoid it because I feel like it&rsquo;s a bigger lump of complexity than I want to take on.</p> <p>I&rsquo;d be more inclined to do sum types and destructuring, even though it feels a little weird in an imperative language, then do this kind of complex control flow analysis.</p> Type Checking If Expressions http://journal.stuffwithstuff.com/2023/01/03/type-checking-if-expressions/ Tue, 03 Jan 2023 08:00:00 GMT [email protected] (Robert Nystrom) http://journal.stuffwithstuff.com/2023/01/03/type-checking-if-expressions <p>I have this hobby project I&rsquo;ve been hacking on for several years. It&rsquo;s a fantasy console, very much inspired by the delightful <a href="https://www.lexaloffle.com/pico-8.php">PICO-8</a>. Like PICO-8, my console has its own built-in scripting language. Because I&rsquo;m me, I of course took the opportunity to design an entirely new language.</p> <p>My goal for the project is a fun way to build small-ish 2D games. I want its scripting language to be expressive enough to be joyful, but small enough that you can learn the whole language and never need to consult a reference manual after that. My dream is a goofy little pixellated IDE where you can get lost in your own flow state and just make shit without having to periodically hit StackOverflow and then get distracted by the wonders/horrors of the Internet.</p> <p>I don&rsquo;t know if I&rsquo;ll ever pull this off or the language will ever see the light of day, but it&rsquo;s a fun therapeutic thing for me to noodle on.</p> <h2 id="a-dynamically-typed-scripting-language"><a href="#a-dynamically-typed-scripting-language">A dynamically typed scripting language<span class="anchor">#a-dynamically-typed-scripting-language</span></a></h2> <p>To make a language that fits in your head (or at least my head, whose working space seems to get smaller every year), I needed to jettison as many feature as I could. My experience across a range of hobby and <a href="https://dart.dev/">not-so-hobby</a> languages is that static types add roughly an order of magnitude of complexity, so types were one of the first things to go. Like most scripting languages, I made mine dynamically typed.</p> <p>Here&rsquo;s an example:</p> <pre class="highlight language-vgs"><span class="k">def</span> <span class="i">onTick</span><span class="p">()</span> <span class="k">var</span> <span class="i">d</span> <span class="o">=</span> <span class="n">0</span> <span class="k">if</span> <span class="i">buttonHeld</span><span class="p">(</span><span class="n">2</span><span class="p">)</span> <span class="k">then</span> <span class="i">d</span> <span class="o">=</span> <span class="i">d</span> <span class="o">-</span> <span class="n">1</span> <span class="k">end</span> <span class="k">if</span> <span class="i">buttonHeld</span><span class="p">(</span><span class="n">3</span><span class="p">)</span> <span class="k">then</span> <span class="i">d</span> <span class="o">=</span> <span class="i">d</span> <span class="o">+</span> <span class="n">1</span> <span class="k">end</span> <span class="k">if</span> <span class="i">d</span> <span class="o">!=</span> <span class="n">0</span> <span class="k">then</span> <span class="i">h</span> <span class="o">=</span> <span class="i">h</span> <span class="o">+</span> <span class="i">d</span> <span class="k">else</span> <span class="k">if</span> <span class="i">h</span> <span class="o">&gt;</span> <span class="n">0</span> <span class="k">then</span> <span class="i">h</span> <span class="o">=</span> <span class="i">h</span> <span class="o">-</span> <span class="n">0.5</span> <span class="k">else</span> <span class="k">if</span> <span class="i">h</span> <span class="o">&lt;</span> <span class="n">0</span> <span class="k">then</span> <span class="i">h</span> <span class="o">=</span> <span class="i">h</span> <span class="o">+</span> <span class="n">0.5</span> <span class="k">end</span> <span class="k">if</span> <span class="i">h</span> <span class="o">&lt;</span> <span class="o">-</span><span class="n">3.0</span> <span class="k">then</span> <span class="i">h</span> <span class="o">=</span> <span class="o">-</span><span class="n">3.0</span> <span class="k">end</span> <span class="k">if</span> <span class="i">h</span> <span class="o">&gt;</span> <span class="n">3.0</span> <span class="k">then</span> <span class="i">h</span> <span class="o">=</span> <span class="n">3.0</span> <span class="k">end</span> <span class="i">x</span> <span class="o">=</span> <span class="i">x</span> <span class="o">+</span> <span class="i">h</span> <span class="k">if</span> <span class="i">y</span> <span class="o">&lt;</span> <span class="n">200</span> <span class="k">then</span> <span class="i">v</span> <span class="o">=</span> <span class="i">v</span> <span class="o">+</span> <span class="n">0.8</span> <span class="k">end</span> <span class="i">y</span> <span class="o">=</span> <span class="i">y</span> <span class="o">+</span> <span class="i">v</span> <span class="k">if</span> <span class="i">y</span> <span class="o">&gt;</span> <span class="n">200</span> <span class="k">then</span> <span class="i">y</span> <span class="o">=</span> <span class="n">200</span> <span class="i">v</span> <span class="o">=</span> <span class="n">0</span> <span class="k">end</span> <span class="k">if</span> <span class="i">buttonPressed</span><span class="p">(</span><span class="n">0</span><span class="p">)</span> <span class="k">then</span> <span class="k">if</span> <span class="i">y</span> <span class="o">==</span> <span class="n">200</span> <span class="k">then</span> <span class="i">playSequence</span><span class="p">()</span> <span class="i">v</span> <span class="o">=</span> <span class="o">-</span><span class="n">10.0</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span></pre> <p>Another simplification I made is to eliminate the distinction between statements and expressions. As in Ruby, Kotlin, and most functional languages, everything is an expression. The previous chained <code>if</code> could be written in a more explicitly expression-y style like:</p> <pre class="highlight language-vgs"><span class="i">h</span> <span class="o">=</span> <span class="i">h</span> <span class="o">+</span> <span class="k">if</span> <span class="i">d</span> <span class="o">!=</span> <span class="n">0</span> <span class="k">then</span> <span class="i">d</span> <span class="k">else</span> <span class="k">if</span> <span class="i">h</span> <span class="o">&gt;</span> <span class="n">0</span> <span class="k">then</span> <span class="o">-</span><span class="n">0.5</span> <span class="k">else</span> <span class="k">if</span> <span class="i">h</span> <span class="o">&lt;</span> <span class="n">0</span> <span class="k">then</span> <span class="n">0.5</span> <span class="k">end</span></pre> <p>Unifying statements and expressions means the language doesn&rsquo;t need a separate <code>if</code> statement and conditional expression. Also, I don&rsquo;t know, I just like expression-oriented languages.</p> <h2 id="an-imperative-language"><a href="#an-imperative-language">An imperative language<span class="anchor">#an-imperative-language</span></a></h2> <p>Even though the language is expression-oriented, it&rsquo;s not explicitly <em>functional</em>. Functional languages are close to my heart, but this is a game scripting language. A game world is basically a big ball of incrementally updated mutable state. For the kind of programs and user experience I have in mind, I think an imperative, procedural style is easy to learn, and fun to program in. I want users thinking about their game, not, like, monads and persistent data structures.</p> <p>So while everything is an expression in my language, it doesn&rsquo;t at all shy away from side effects and imperative control flow. Variables are assignable. Fields are settable. There are loops and breaks and early returns. All of those are as natural and idiomatic as they are in C++, JavaScript, C#, or any of the other languages that the majority of the world&rsquo;s code is written in.</p> <h2 id="handmade-seattle"><a href="#handmade-seattle">Handmade Seattle<span class="anchor">#handmade-seattle</span></a></h2> <p>Last fall, I attended the wonderful <a href="https://handmade-seattle.com/">Handmade Seattle</a> conference. I had a particularly inspiring conversation with <a href="https://wiki.xxiivv.com/site/home.html">Devine Lu Linvega</a> about their tiny <a href="https://wiki.xxiivv.com/site/uxn.html">uxn</a> VM. They had this idea to build the smallest possible system and programming language for their own use. Then they rebuilt their own personal tools&mdash;text editor, music stuff, etc.&mdash;using that.</p> <p>Now, UXN is <em>really</em> minimal. I get a certain satisfaction from programming in assembly, but it&rsquo;s not the language I would want to use for my own joy. But it did make me rethink the scripting language for my fantasy console. I picked dynamic types because that made the language smaller and I figured it would be a good fit for my (entirely hypothetical at this point) users.</p> <p>But is it what <em>I&rsquo;d</em> want to use to make little 2D videogames? The game I&rsquo;ve spent the most time hacking on is my also-perennially-incomplete roguelike <a href="https://github.com/munificent/hauberk">Hauberk</a>. I&rsquo;ve rewritten it several times, but every incarnation has been in a statically typed language: C++, C#, Java, and now Dart.</p> <p>My most pleasurable time spent working on Hauberk is when I&rsquo;m refactoring and the type system guides me to what&rsquo;s left to clean up. I just really like working with types. (It&rsquo;s OK if you don&rsquo;t. As our Burger Sovereign says, have it your way.)</p> <p>After talking to Devine, I realized that if I was making this fantasy console <em>for me personally</em>, its language would be typed. So over the past few weeks, I&rsquo;ve been sketching out a statically typed variant of my console&rsquo;s scripting language. I don&rsquo;t know if it will really come together, but I thought maybe it would be fun to write about the exploration.</p> <h2 id="type-checking-if-expressions"><a href="#type-checking-if-expressions">Type checking <code>if</code> expressions<span class="anchor">#type-checking-if-expressions</span></a></h2> <p>I slapped together a new prototype interpreter for my language. (The main implementation is a bytecode VM in C++, which is pretty fast but not exactly easy to hack on.) Then I dutifully started adding a type checking pass to it. One of the first challenges I hit is how to type check <code>if</code> expressions.</p> <p>As the title up there implies, that&rsquo;s what this post is really about. Because it turns out that having <code>if</code> be an expression while also fully embracing an imperative style gets a little weird when it comes to type checking.</p> <p>I&rsquo;ll walk through a bunch of examples and build up to the type checking rules I have settled on (so far, at least). We&rsquo;ll start simple:</p> <pre class="highlight language-vgs"><span class="k">var</span> <span class="i">love</span> <span class="o">=</span> <span class="k">if</span> <span class="i">isFriday</span> <span class="k">then</span> <span class="s">&quot;in love&quot;</span> <span class="k">else</span> <span class="s">&quot;not in love&quot;</span> <span class="k">end</span></pre> <p>We need a type for the <code>if</code> expression so that we can infer a type for the variable <code>love</code>. In this case, the type is obviously String since both the then and else branches evaluate to strings.</p> <p>So the basic rule we&rsquo;ll start with is: <strong>An <code>if</code> expression&rsquo;s type is the type of the branches.</strong></p> <h2 id="different-branch-types"><a href="#different-branch-types">Different branch types<span class="anchor">#different-branch-types</span></a></h2> <p>But what if they don&rsquo;t have the same type? What about:</p> <pre class="highlight language-vgs"><span class="k">var</span> <span class="i">love</span> <span class="o">=</span> <span class="k">if</span> <span class="i">isFriday</span> <span class="k">then</span> <span class="s">&quot;in love&quot;</span> <span class="k">else</span> <span class="n">0</span> <span class="k">end</span></pre> <p>Here, <code>love</code> could end up being initialized to either a <code>String</code> or an <code>Int</code>. Now what type do we choose? <a href="https://crystal-lang.org/reference/1.7/syntax_and_semantics/if.html">Crystal&rsquo;s answer</a> is <code>String | Int</code>. Union types are cool but definitely too complex for the language I&rsquo;m trying to make.</p> <p>In Kotlin, which is also typed and expression-oriented, the answer is, apparently, <code>{Comparable&lt;CapturedType(*)&gt; &amp; java.io.Serializable}</code>. Which I have to say does not seem <em>super</em> helpful.</p> <p>I assume that the compiler goes looking for a shared supertype of the two branch types, String and Int. Since String and Int both happen to implement Comparable (and I guess some serialization interface), you get that as the common supertype.</p> <p>In object-oriented languages with subtyping and where the type hierarchy forms a <a href="https://en.wikipedia.org/wiki/Lattice_(order)">lattice</a>, this common supertype is the least upper bound, and it&rsquo;s a natural answer to the problem. It shows up in other languages when type-checking conditional <code>?:</code> expressions and a few other places.</p> <p>It works, but, as we can see in the Kotlin example here, it doesn&rsquo;t always produce intuitive or useful results. More to the point, one of the <em>other</em> features I jettisoned from my scripting language is subtyping, so LUB is off the table.</p> <p>Without subtyping, every type is disjoint: a value of one type is never a value of any other type too. That means that if the two branches of an <code>if</code> have different types, then there is no possible type I can infer that contains all of their values. The only other response is to make it a type error.</p> <p>That&rsquo;s the next rule: <strong>If the branches have different types, it&rsquo;s a compile error.</strong></p> <h2 id="imperative-ifs-and-unused-values"><a href="#imperative-ifs-and-unused-values">Imperative ifs and unused values<span class="anchor">#imperative-ifs-and-unused-values</span></a></h2> <p>That rule does work: It&rsquo;s basically SML&rsquo;s rule for <code>if</code> expressions. But I want my scripting language to feel familiar to users programming in an imperative style. Consider:</p> <pre class="highlight language-vgs"><span class="k">var</span> <span class="i">daysNotInLove</span> <span class="o">=</span> <span class="n">0</span> <span class="k">if</span> <span class="i">isFriday</span> <span class="k">then</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;in love&quot;</span><span class="p">)</span> <span class="k">else</span> <span class="i">daysNotInLove</span> <span class="o">=</span> <span class="n">1</span> <span class="k">end</span></pre> <p>Here, the two branches have different types. The then branch has type String because in my language, <code>print()</code> returns its argument. (That makes it handy for stuffing some debug printing in the middle of an expression.) The else branch has type Int because an assignment expression yields the assigned value.</p> <p>According to the previous rule, this is a type error because we don&rsquo;t know what type of value the <code>if</code> expression evaluates to.</p> <p>But it doesn&rsquo;t <em>matter</em> since the <code>if</code>&rsquo;s value isn&rsquo;t being used anyway. There&rsquo;s no need for the compiler to yell at you, and code like this turns out to be very common in practice.</p> <p>To address this, the type checker takes some of the surrounding context into account. When an <code>if</code> expression appears in a location where its value won&rsquo;t be used, then it&rsquo;s no longer an error for the branches to have different types. How complex is tracking that context? Not too bad, actually. There are a handful of cases:</p> <ul> <li> <p>In a block or function body where you have a sequence of expressions, the result is the value of the last expression. The values of all of the preceding expressions are discarded. So in an expression sequence, all but the last expression are in a &ldquo;value not used&rdquo; context.</p> </li> <li> <p>Like other expression-oriented languages, functions in my language implicitly return the value that the function body expression evaluates to:</p> <pre class="highlight language-vgs"><span class="k">def</span> <span class="i">three</span><span class="p">()</span> <span class="t">Int</span> <span class="i">print</span><span class="p">(</span><span class="s">&quot;About to return three...&quot;</span><span class="p">)</span> <span class="n">3</span> <span class="k">end</span> <span class="k">def</span> <span class="i">onInit</span><span class="p">()</span> <span class="i">print</span><span class="p">(</span><span class="i">three</span><span class="p">())</span> <span class="c"># Prints &quot;About to return three...&quot; then &quot;3&quot;.</span> <span class="k">end</span></pre> <p>But if a function has no return type (the same as <code>void</code> or unit in other languages), it doesn&rsquo;t return a value. In that case, even the last expression in the body is a &ldquo;value not used&rdquo; context.</p> </li> <li> <p>Loop expressions don&rsquo;t produce values, so their body is always a &ldquo;value not used&rdquo; context. (I&rsquo;m toying with the idea of allowing <code>break</code> expressions to yield a value from the loop, but they don&rsquo;t right now.)</p> </li> <li> <p>Whenever an <code>if</code> or <code>match</code> expression is in a &ldquo;value not used&rdquo; context, then we push that context into the branches too. Likewise with the right-hand side of <code>and</code> and <code>or</code> logic operators since those are control flow expressions to.</p> </li> </ul> <p>That&rsquo;s it. After I came up with this rule, I did some poking around and it seems like Kotlin does something similar. It frames it by saying that when you use an <code>if</code> &ldquo;as an expression&rdquo; then the two branches must have the same type. That&rsquo;s roughly the distinction I&rsquo;m making here too: when an <code>if</code> appears in a statement-like position where its value is discarded, then the branches can disagree.</p> <h2 id="missing-else"><a href="#missing-else">Missing else<span class="anchor">#missing-else</span></a></h2> <p>This rule allows us to support an even more important flavor of <code>if</code> expressions that are common in imperative code: those without <code>else</code> clauses. In SML and some other functional languages, every <code>if</code> expression <em>must</em> have an <code>else</code> clause because the presumption is that you will be using the value produced by the expression and you need a value even when the condition is false.</p> <p>But in imperative code, it&rsquo;s obviously common to have <code>if</code>s whose main purpose is a side effect and where an <code>else</code> clause isn&rsquo;t needed. In fact, when I analyzed a huge corpus of real-world Dart, I found that only about 20% of <code>if</code> statements had <code>else</code> branches.</p> <p>Now that we understand when an <code>if</code> expression is in a context where it&rsquo;s value isn&rsquo;t used, we can allow omitting <code>else</code> branches those. The next rule is: <strong>An <code>if</code> expression can omit the else branch when in a context where its value isn&rsquo;t used.</strong></p> <h2 id="exiting-branches"><a href="#exiting-branches">Exiting branches<span class="anchor">#exiting-branches</span></a></h2> <p>We&rsquo;re almost there. It&rsquo;s starting to feel like we really are type-checking an imperative language, not ML in BASIC&rsquo;s clothing. I coded this up and successfully wrote some little example programs. It was starting to feel like a real typed language!</p> <p>I could stop here, but there&rsquo;s one last bit of type checking logic for <code>if</code> expressions. I haven&rsquo;t decided if it&rsquo;s worth keeping. Consider:</p> <pre class="highlight language-vgs"><span class="k">def</span> <span class="i">onInit</span><span class="p">()</span> <span class="k">var</span> <span class="i">love</span> <span class="o">=</span> <span class="k">if</span> <span class="i">isFriday</span> <span class="k">then</span> <span class="s">&quot;in love&quot;</span> <span class="k">else</span> <span class="k">return</span> <span class="k">end</span> <span class="k">end</span></pre> <p>When <code>isFriday</code> is true, then this initializes <code>love</code> with the string &ldquo;in love&rdquo;. When <code>isFriday</code> is false, then the <code>return</code> exits from the function entirely so <code>love</code> never gets initialized at all. So even though these branches don&rsquo;t evaluate to the same type, <code>love</code> is always initialized with a String. This code should be fine.</p> <p>Or, at least, it should be <em>sound</em> according to the type system. Whether this is <em>good style</em> is definitely open for debate. I could probably not allow code like this. But my default stance is to be as permissive as possible without breaking soundness, and this is a corner where I can be.</p> <p>The trick is that expressions like <code>break</code>, <code>return</code>, and <code>throw</code> are special. While they are expressions <em>grammatically</em>, they don&rsquo;t actually evaluate to values. If you do:</p> <pre class="highlight language-vgs"><span class="k">var</span> <span class="i">x</span> <span class="o">=</span> <span class="k">return</span></pre> <p>That <code>x</code> never gets initialized. A <code>return</code> expression always jumps out of the surrounding code instead of producing a value. Languages that have expressions which can do control flow model this by giving these expressions a special type variously called <a href="https://en.wikipedia.org/wiki/Bottom_type">&ldquo;bottom&rdquo;, <code>⊥</code> (&ldquo;up tack&rdquo;), <code>Never</code>, <code>noreturn</code>, etc</a>. This type means &ldquo;You&rsquo;re never gonna get a value from me.&rdquo;</p> <p>When checking the two branches of an <code>if</code> expression, if one branch has that special type (the compiler calls it &ldquo;unreachable&rdquo; right now), then we just use the type of the other branch for the <code>if</code> expression&rsquo;s type. That allows the above example to work. In the sample code I&rsquo;ve written so far, it rarely comes into play. It&rsquo;s usually more idiomatic to hoist that control flow out of the <code>if</code> entirely. But we can type check it easily, so the language lets you do it.</p> <h2 id="the-rules-altogether"><a href="#the-rules-altogether">The rules altogether<span class="anchor">#the-rules-altogether</span></a></h2> <p>That&rsquo;s where I&rsquo;m at right now. It took me a few iterations to get to a point where all the <code>if</code> expressions I expected to be able to write in my example programs actually type checked correctly but it seems pretty stable now. The rules are:</p> <ul> <li> <p>When an <code>if</code> expression is in a context where its value is not used, then there is no restriction on what types the branches can have and we&rsquo;re done.</p> </li> <li> <p>Otherwise, there must be an <code>else</code> branch and:</p> </li> <li> <p>If both branches have type &ldquo;unreachable&rdquo; then the <code>if</code> expression&rsquo;s type is also &ldquo;unreachable&rdquo;.</p> </li> <li> <p>If one branch has type &ldquo;unreachable&rdquo; then the <code>if</code> expression&rsquo;s type is the type of the other branch.</p> </li> <li> <p>Otherwise, the two branches must have the same type and the type of the <code>if</code> is that type.</p> </li> </ul> Stupid Dog http://journal.stuffwithstuff.com/2022/02/13/stupid-dog/ Sun, 13 Feb 2022 08:00:00 GMT [email protected] (Robert Nystrom) http://journal.stuffwithstuff.com/2022/02/13/stupid-dog <p>It&rsquo;s my 30th birthday, and Megan and I are walking down Duval Street in Key West. There is a pet store here, which must be the most unlikely place for a pet store in all the world. Everyone here is a tourist. What kind of idiot buys a dog while on vacation?</p> <p>You are in the window surrounded by other puppies. A tottering orange ball of fuzz with little triangular ears, like a fox who lost its tail. Ink-black eyes and round button nose. My wife, pregnant and hormonal, is instantly in love with you. We go inside to play with the puppies, because why would you not want to play with cute puppies while on vacation? You curl up in my cupped hands, calm and sleepy.</p><figure> <img class="framed" src="/image/2022/02/01.jpg"> </figure> <p>Megan gives me a lingering series of Significant Looks, which I do my best to ignore. I generally try to avoid making dumb decisions, and impulsively buying a puppy while on vacation eight hours away from home is clearly in that category.</p> <p>I make a silent vow to never let Megan know that I also fell in love with you the moment I saw you.</p> <hr /> <p>It&rsquo;s the next day and Megan and I are sitting at a Burger King across the street from the pet store. She is presenting her case for why we should buy this very cute half-Pomeranian half-Yorkie puppy even though:</p> <ol> <li> <p>We are on vacation.</p> </li> <li> <p>We are expecting our first child in a couple of months and already have more than enough to keep us busy.</p> </li> <li> <p>We have absolutely no plans or intentions on getting a pet. Our house is <em>tiny</em> and barely fits the two of us and two cats, much less a baby and a dog.</p> </li> <li> <p>We are an eight-hour drive from home.</p> </li> <li> <p>You are, in fact, quite expensive.</p> </li> </ol> <p>Do I even need to write the numbered list here? It&rsquo;s obviously a terrible idea. Family members that we&rsquo;re vacationing with look at us like we are idiots when we say we&rsquo;re considering it.</p> <p>I think very very hard about my beloved first dog, Snickers, named because she was chocolate brown with swirls of white and caramel. She was a Sheltie, the smallest of her litter, a funny little runt of a puppy who entered my life when I was feeling small and cast out too.</p> <p>When I was seven, I was playing in a neighbor&rsquo;s front yard while she was out with me. I got distracted and heard a screech of tires behind me. When I turned around, Snickers was on her back motionless in the street. I carried her limp body all the way back to my house. The shocked look on my mother&rsquo;s face when she saw me arrive, tears streaming down my face, broken dog in my arms, is my most vivid childhood memory.</p> <p>I am the kind of person who analyzes 100 steps ahead. Getting a puppy means buying a ticket for the whole ride, including the end of it, in whatever form it takes. I am choosing to break my own heart. What if it&rsquo;s not worth all that pain?</p> <hr /> <p>Megan is driving us north from Miami. You are sleeping in my lap, impossibly warm, cold wet nose pressed against my arm. We talk about names. You almost end up &ldquo;Dorito&rdquo; because of your triangular orange ears, but it doesn&rsquo;t stick. Instead, you become &ldquo;Ginny&rdquo;. Not for Ginny Weasely like everyone assumes but short for the liquor genever.</p> <hr /> <p>When we buy you, I also buy some books on dog training because I am the kind of person who wants to be a very good dog owner that follows all the dog owning rules. Rule #1 is crate training starts immediately. A dog won&rsquo;t respect you as the pack leader if they are free to sleep where you do. They&rsquo;ll think they run the pack.</p> <p>It&rsquo;s late at night by the time we get home, and all the pet stores have closed. Next to the bed, I try to improvise some crate-like thing for your first night involving cardboard boxes, a cooler, and anything else I can find around the house. You yowl in terror, or perhaps just disappointment at the poor accommodations.</p> <p>Eventually, I give up. I pick you up and lay you on my chest. You are asleep instantly, tiny heart fluttering against mine.</p> <hr /> <p>You are taking your sweet time exploring every single corner of the jungle that is our backyard, looking for lizards. It is three o&rsquo;clock in the morning. The green and brown anoles are sleeping under leaves where you never find them. I am shambling after you in pajama pants and old boots telepathically trying to get you to pee with every ounce of willpower I have.</p> <p>I am keenly aware of my dwindling opportunities to get a good night&rsquo;s sleep before the baby arrives. Yet here we are. Over the past few weeks Megan and I have mostly learned to tell the difference between your yowl that means &ldquo;I am a puppy and don&rsquo;t like being alone in this crate&rdquo; and the yowl that means &ldquo;My bladder is too small to last the night and I need to pee right now.&rdquo; Sorry about the mistakes we made while figuring that out.</p> <p>I pee in the yard too, hoping you&rsquo;ll get the hint. I deeply regret ever seeing you in that pet store window. Half-awake, I have vivid fantasies of building a time machine, traveling back in time, and slapping myself in the middle of that Key West Burger King.</p> <p>After patrolling the perimeter of the yard in tiny puppy steps, twice, you finally pee. I tell you what a good girl you are and give you a treat.</p> <hr /> <p>Because you are an adorable tiny puppy and I am a nerd with a DSLR, I take copious photos of you and upload them to Flickr. One day, I get an email from cuteoverload.com asking if they can share some of them. The next day, you are Internet famous. Millions of people look at your butthole.</p><figure> <img class="framed" src="/image/2022/02/02.jpg"> </figure> <p>You are oblivious to your celebrity, as you are to most things in life.</p> <hr /> <p>As you grow, your puppy fuzz is replaced by silk, still orange on top and white on the bottom like a fox. The whorls of fur behind your ears are the softest thing I have ever felt in my life. I run my fingers through them constantly, to your delight.</p> <hr /> <p>I take you on walks every day, still trying to be some kind of adequate dog owner. The training manuals say that to teach a puppy walk properly on a leash you should stop walking whenever the dog pulls against it. Then wait patiently until the dog turns and makes eye contact with you. That way, they learn that you are in charge of walking and that pulling means the walk ends.</p> <p>It takes us half an hour to walk a single block. Despite repeating this process hundreds of times, you fail to make the mental connection that if you stop yanking on the leash then we can actually walk where you want to go. You never, ever stop pulling on the damn leash.</p> <p>I start a running joke of whispering insults in your ear that sound like terms of endearment. It&rsquo;s not like you speak English, so when I say, &ldquo;I hate your face,&rdquo; all you hear is the caring tone I use to say it. &ldquo;You&rsquo;re so fucking stupid,&rdquo; I coo into your ear. I rub your fuzzy belly to make sure you know how much I love your stupid face.</p><figure> <img class="framed" src="/image/2022/02/03.jpg"> </figure> <hr /> <p>Five weeks earlier than expected, Megan and I become parents. We haven&rsquo;t had time to set up a nursery yet so we hastily buy a bassinet. The first night the baby is home from the NICU, we realize there isn&rsquo;t enough room at the foot of the bed for both the bassinet and the crate.</p> <p>We let you sleep on the bed. You are immensely proud of earning a place on the bed while the new pink hairless dog is relegated to the funny crate.</p> <hr /> <p>You are listless and frothing at the mouth. Megan and I are terrified. You had been in the yard stalking all of the various animals that live in the barely-tamed-jungle that is Orlando. This time, you found a toad and you&rsquo;d managed to get your mouth on it before I could get to you. Did it poison you? Are you going to die?</p> <p>We rush you to the emergency vet. She takes your vitals while you sit there limp and apathetic. Your blood sugar is dangerously low. She gives you a treat. Then another. You immediately perk up. You&rsquo;re fine, totally fine.</p> <p>I remember that a couple of days prior I put my foot down and decided you were ready to graduate from soft puppy food to kibble which is better for your teeth. You were uninterested in the new food. I confidently told Megan, &ldquo;If she gets hungry enough, she&rsquo;ll eat. It&rsquo;s not like she&rsquo;s going to starve herself.&rdquo;</p> <p>Apparently, somehow during the thousands of years humans spent turning a wolf into the strange-colored, tiny, eternally cub-like thing that you are, we broke your survival instinct. Sorry about that. We learn to mix some wet food into your kibble.</p> <hr /> <p>It is something like four in the morning and Megan and I are pushing a baby stroller and a luggage cart across the SeaTac airport with you and the cats crammed in pet carriers perched on top of our suitcases. We&rsquo;ve just flown 3,000 miles to our new home. Our brilliant idea to take a red-eye flight so the baby would sleep through it failed disastrously. Megan and I spent the entire flight taking turns trying to get the baby to sleep on our laps. You seem to have mostly survived the flight intact. The cats were less thrilled by the experience.</p> <p>We get to the hotel, and I take you out just as the sun starts to come up. The parking lot of an Extended Stay in Redmond is my first view of our new home in the Pacific Northwest. It&rsquo;s fifty degrees and rainy. I&rsquo;m beyond tired. You take your sweet time before finding an acceptable spot to pee.</p> <hr /> <p>We are on PetFinder looking for a second dog. You are just so&hellip; weird. Not food motivated. Impossible to train. High strung. Frankly, not very smart. You bark all the time, at everything. We tell ourselves that a second dog will give you company when we&rsquo;re out. They will teach you how to dog like a normal dog.</p> <p>We find a part-Chihuahua part rat terrier that is, in defiance of everything I understand about genetics, practically your twin. We bring you to meet him at a dog park to see if you get along. You bark a lot. He tolerates you admirably well. For the rest of your lives, you will be his asshole boss. You&rsquo;ll eat the best parts out of his food bowl and steal his treats. When he tries to sit next to us on the couch, you will wedge yourself in the middle and glare at us both.</p> <p>He will live in fear of your ire which is capricious and often. We name him Benny for &ldquo;beignet&rdquo; because he&rsquo;s golden with a sprinkling of white on top.</p> <p>Maybe they were right about the crate training.</p> <hr /> <p>We rent a house out in the sticks. There is a green belt behind the yard and we get deer sometimes. Occasionally, we hear an owl hoot. Inexplicably, you are absolutely terrified of the sound. Megan and I hoot at you to tease you and you bark back at us, shivering in fear.</p> <p>Later, we buy a house with a fenced in back yard. You stand proudly in the grass waiting for me to make eye contact with you. The moment I do, you throw your head back in delight and run full tilt to me. Over and over again from Megan to me and back. You wear yourself out and lay in the sun panting &ldquo;keh keh keh&rdquo;. When you snuggle with us on the couch later, you still smell like dirt and grass.</p> <hr /> <p>I am in the basement awkwardly taking photos of myself. I&rsquo;m speaking at a conference and they asked for a headshot. You wander in and I pick you up and start taking pictures of us together. They are my favorite photos of myself.</p><figure> <img class="framed" src="/image/2022/02/04.jpg"> </figure> <p>When I finish my writing my book, I put one of them on the back cover. I get email from readers telling me how delighted they are with the cover.</p> <p>I set that photo as my profile image on all of my various accounts. When ride share drivers pick me up, they joyfully tell me in not-great English that they love my dog. You are still a celebrity.</p> <hr /> <p>I am in bed staring at the dark ceiling, doing math in my head. Tonight, you are the same age in dog years that I am in real years. From this moment on, you are older than me. I picture you racing ahead of me into the future. I try not to think of where that path leads or when you&rsquo;ll get there.</p> <p>You are sleeping stretched out, pressed against my thigh, trying to get every square millimeter of contact that you can. I run my fingers through the silky fur on your back, feel your breath rise and fall.</p> <hr /> <p>We rent a cabin on the Pacific coast. You spend the first half of the drive shaking in excitement and anxiety, paws on the door looking out the window. The whole car smells like your horrid breath and Megan is coated in orange fur. Eventually you wear yourself out and pass out in her lap, head draped over her arm towards me. I rub the fur between your ears while I drive.</p><figure> <img class="framed" src="/image/2022/02/05.jpg"> </figure> <p>After settling in, we go explore tide pools. You sniff everything. Every rock, bit of kelp, rotting fish, even the wind. It&rsquo;s cold and wet, but you don&rsquo;t mind. When we get back to the cabin, I wipe sand and seaweed from your paws. You fall asleep in front of the fireplace.</p> <hr /> <p>The vet is explaining to us that you have hyperthyroidism and congestive heart disease. That&rsquo;s why you pee all the time&mdash;often in the house&mdash;and why you seem so tense and wound up all the time. Well, that and because you are half Pomeranian.</p> <p>You get put on an expensive pill regimen which you will take for the rest of your life. Since you aren&rsquo;t interested in food even on a good day, getting you to take them will be a constant battle. At least the peeing inside gets marginally better. The barking does not.</p> <hr /> <p>I start writing a second book. I work on my laptop on the couch and you lay pressed against one leg the entire time. Once you&rsquo;ve fallen asleep and let down your jealous guard over access to me, Benny claims the other side.</p> <hr /><figure> <img class="framed" src="/image/2022/02/06.jpg"> </figure> <p>We take another trip out to the Olympic Peninsula with friends. You spend much of it sleeping on the couch in front of the fire, like an overweight self-warming throw pillow. You are given affection from everyone, which you regard as your proper due. In return, you grace us with an endless series of farts that leave the cabin smelling like some sort of reptilian tire fire.</p> <hr /> <p>It&rsquo;s six o&rsquo;clock in the morning. I am barely awake making coffee while you are outside for your morning consitutional. I hear a strange sort of yelp at the back door. I open it just in time to see you collapse. I scoop you up and run into the dining room, yelling out for Megan. I sit on the floor and cradle you, rocking back and forth. If these are your last moments, I want them to be peaceful.</p> <p>I hold your limp body in my arms. Your tongue hangs out, lifeless and blue. I am seven years old again.</p> <p>After an endless minute, you come back to us. Your back stiffens. Your legs poke out like a fainting goat and then relax. Your eyes start darting around and eventually you are able to pick your head up. Ten minutes later and you are back to normal, or at least as normal as you ever are. Honestly, a loss in cognitive function would be hard to detect.</p> <p>The vet begins to use the phrase &ldquo;congestive heart <em>failure</em>&rdquo; now. The pill regimen gets more complex. We throw out yet another rug too pee-stained to repair and decide to not have a rug in the dining room &ldquo;for now&rdquo;.</p> <hr /> <p>You are mostly enjoying your retirement. You spend so much time laying on the back of the couch so that you can look out the back window and bark at anyone with the audacity to walk on the sidewalk that the cushion loses its shape entirely.</p> <p>We alternate between trying to give you as much affection as we can in the time we have left and cursing your name as we discover another part of the house ruined by your sneaky peeing. I discover right in the middle of a push-up, my nose an inch from the floor, that you and Benny have surreptitiously saturated every inch of the carpet in the office. Megan and I learn how to put in new flooring.</p> <hr /> <p>We enter the phase where you take medications to cope with the side effects of other medications. You get over a dozen different pills a day, each on their own specific schedule. I print a chart and put it on the fridge to make sure I don&rsquo;t miss any.</p> <p>It is exhausting, but mostly for us. You are still relatively content, lazing around the house. Stealing Benny&rsquo;s food. Barking at people outside the window.</p><figure> <img class="framed" src="/image/2022/02/07.jpg"> </figure> <p>Sometimes, at night, you crawl between my legs on top of the blankets, pinning me and forcing me to sleep on my back. I tolerate it as best I can. You&rsquo;re warm and your ears are still velvet-soft.</p> <hr /> <p>One morning, I wake up to discover you have leaked little brown droplets all over the blanket. You are sitting up in bed, panting, clearly uncomfortable. (I&rsquo;m not exactly comfortable with the situation myself.)</p> <p>A couple of days later, you faint again. Perhaps a stroke. Dog.exe has stopped working. When you finish your reboot cycle, you aren&rsquo;t the same. You don&rsquo;t recognize Megan and snap at her viciously. You stand stock still in the middle of the dining room floor, eyes glazed.</p> <p>It&rsquo;s clearly time. I feel an unexpected relief. For years, I have held a deep fear that I wouldn&rsquo;t know when it was over. That I wasn&rsquo;t adult enough to handle the responsibility of making the call. I&rsquo;d procrastinate too long and leave you in misery or choose prematurely and cut your life short. Or I would just be wracked in anguish the whole time. Your last gift to me is a swift decline so that there&rsquo;s no doubt.</p> <p>I learn there is an entire category of vet-adjacent businesses that specialize in this &ldquo;transition&rdquo;. Early on a Tuesday morning, I start calling them to see who can come out today, as soon as possible, right now. The women who answer the phone have well-rehearsed, soothing voices. I stumble over my words. I don&rsquo;t know the right social protocol to ask, &ldquo;Can you please come kill my dog?&rdquo;</p> <hr /> <p>You are sitting next to me on the couch, pressed warmly against my side like always. You&rsquo;ve mostly calmed down but are sitting up and tense. A kind woman radiating Pacific Northwest Earth Mother vibes is sitting on the other side of you with a small array of syringes in front of her on the coffee table. We&rsquo;ve tucked a blanket under you.</p> <p>The kids have already said goodbye to you, tears pouring down their faces. Surprisingly, that turns out to be the hardest part, realizing how much you are part of their stories too.</p> <p>The first shot makes you fall asleep. Literally, not in the &ldquo;put to sleep&rdquo; figurative sense. As the woman pushes the plunger, you loll slowly over onto my leg. You&rsquo;re napping, head on my thigh, more comfortable than I&rsquo;ve seen you in weeks. Like old times.</p> <p>I run my fingers through the fur on your back, up to where it gets shorter and velvety between your ears, around your side where it gets thinner and silkier, to your chest where it thickens into whorls. I leave my fingers there where I can feel your heart beating. I want to <em>be here</em> for it, to know that I was with you from beginning to end.</p> <p>When the second shot goes in, your music stops.</p> <p>With practiced timing, the woman has your body wrapped up in the blanket and out the door in minutes. The second the door closes, I feel the mask fall off my face and the tears start.</p> <hr /> <p>It&rsquo;s later that day. I&rsquo;ve thrown out the pills, pill poppers, prescriptions, aftercare instructions, wrecked towels and blankets, every reminder of your failing health.</p> <p>I am out for a walk to get out of the house. It hits me a few blocks away. The answer to the question I carried since the day we got you. Will the life we have with you outweigh the pain at the end? Will it be worth it?</p> <p>It is. Every minute.</p> <hr /> <p>It&rsquo;s a couple of years later. Your ashes are in a little box in my nightstand. I remember the stress and exhaustion of the last few months of your life, but it feels vague, like I remember the stories I told people about it but not the actual experience.</p> <p>I remember all of the good moments with you vividly. The feel of your fur between my fingers, your body pressed against my side, your horrific breath panting in my face. Megan and I talk about your antics.</p> <p>I still miss you and your stupid face.</p><figure> <img class="framed" src="/image/2022/02/08.jpg"> <figcaption>Ginny 2008&hairsp;&ndash;&hairsp;2019</figcaption> </figure> 640 Pages in 15 Months http://journal.stuffwithstuff.com/2021/07/29/640-pages-in-15-months/ Thu, 29 Jul 2021 07:00:00 GMT [email protected] (Robert Nystrom) http://journal.stuffwithstuff.com/2021/07/29/640-pages-in-15-months <p>My book <a href="http://craftinginterpreters.com/"><em>Crafting Interpreters</em></a> on programming languages is done. OK, OK. I know <a href="/2020/04/05/crafting-crafting-interpreters/">I said it was done like fifteen months ago</a>. But now it&rsquo;s <em>really</em> done. And by that I mean, the print, e-book and PDF versions are done. <strong>You can <a href="http://craftinginterpreters.com/">buy it</a>.</strong> You can hold it in your hands. And I do mean &ldquo;hands&rdquo; plural. Because this little &ldquo;handbook&rdquo; turned out way larger than I anticipated:</p><figure> <a href="http://craftinginterpreters.com/"><img class="framed" src="/image/2021/07/book.jpg"></a> <figcaption>This is a proof copy, so it looks a little different than the final design.</figcaption> </figure> <p>Look at that thing. 640 pages, eight inches wide, ten inches tall. If you get tired of reading it, it can serve as a doorstop or protect you from small-arms fire.</p> <p>Remember back on Mr. Roger&rsquo;s Neighborhood when he would take you to a factory and show you how pencils or umbrellas are made? I love that stuff, so I thought maybe you might like to see what I spent the past year on. You can read this as a peek behind the curtain, or maybe a long apology for why it took so long.</p> <h2 id="where-i-left-off"><a href="#where-i-left-off">Where I left off<span class="anchor">#where-i-left-off</span></a></h2> <p>What I said in my last post was that <em>Crafting Interpreters</em> was <em>finished</em>, and by that I meant that I&rsquo;d written all the code, dotted the last sentence, and scanned the last illustration. The <em>content</em> of the book was complete. But it wasn&rsquo;t a book yet. Just a pile of Markdown and PNG files that my embarrassingly bad Python code would begrudgingly turn into a website.</p> <p>My goal has always been to make a real book, with pages and everything. Completing the content was just one (granted, the most important) step in that journey. Once I finished the last chapter and put it online, I took about a month-long break. I had been writing <a href="https://github.com/munificent/craftinginterpreters/blob/master/note/log.txt">every single day for almost four years</a> and I was <em>fried</em>. Also, in case you don&rsquo;t remember, early 2020 was not exactly a Totally Fine Time in World History.</p> <h2 id="a-new-build-system"><a href="#a-new-build-system">A new build system<span class="anchor">#a-new-build-system</span></a></h2> <p>After a few weeks, my desire to have this project completely done returned and I eased my way back into working on it. I fixed a whole pile of typos and other mistakes that readers <a href="https://github.com/munificent/craftinginterpreters/issues">filed bugs for</a> (thanks!).</p> <p>Then, for no real good reason at all, I decided to rewrite the whole build system for the book in <a href="https://dart.dev/">Dart</a>. The build script I wrote for <a href="http://gameprogrammingpatterns.com/">my first book</a> was dead simple. <a href="https://github.com/munificent/game-programming-patterns/blob/master/script/format.py">Literally a single Python script</a> that took a Markdown file for each book chapter and rendered it to HTML while weaving in the code snippets. The world&rsquo;s dumbest static site generator.</p> <p>I started with that for <em>Crafting Interpreters</em> but then it grew and grew. My second book includes every single line of code for two complete interpreters, which it builds up incrementally across thirty chapters. I needed to not just build the HTML for the book&rsquo;s website, but also make sure the code really did work. I gave the build system the ability to not only generate the site for the book, but also to slice and dice the code. Given a chapter, or even a single point within a chapter, it can output a program containing all of the code for the interpreters up to that point. Then I can take that code, compile it, and run it through my automated test suite to make sure the code I&rsquo;m showing you does what it&rsquo;s supposed to.</p> <p>Useful, but really straining the limits of how much code I want to maintain in a dynamically typed language like Python, at least, with my (low) level of Python expertise. Also, it was, frankly, really slow. So over a period of a couple of weeks, I rewrote the whole thing in Dart.</p> <p>I work at Google on the <a href="https://dart.dev/">Dart language</a> team as my day job, so picking Dart was not an unbiased choice. But it&rsquo;s my build system for my book, and I know Dart and many of its core libraries and packages like the back of my hand. Hell, I personally wrote the initial version of the canonical <a href="https://pub.dev/packages/markdown">markdown package</a>.</p> <p>I found a pretty nice <a href="https://pub.dev/packages/mustache_template">package for mustache templates</a>, so I converted the book&rsquo;s old Liquid templates to that. I didn&rsquo;t find a good syntax highlighter. But it&rsquo;s not like I looked very hard either. It seemed like just the kind of fun thing to implement from scratch, so I whipped one up loosely based on Pygments.</p> <p>The end result is a new build system that generates <em>exactly</em> the HTML and syntax-highlighted code that I want. Also, it is literally ten <em>times</em> faster than the old Python one. As you&rsquo;ll see, it turned out to be handy that I had better control over the Markdown processing, but at the time I was basically just doing this for fun and to procrastinate the real work.</p><figure> <img class="framed" src="/image/2021/07/speed.png"> <figcaption>I planned to implement some clever incremental rebuild logic in the dev server, but it builds so fast that I just rebuild everything on every refresh.</figcaption> </figure> <p>Once I had the new build system outputting nice clean HTML and I&rsquo;d deleted the old Python stuff, it was time to get started.</p> <h2 id="designing-the-book"><a href="#designing-the-book">Designing the book<span class="anchor">#designing-the-book</span></a></h2> <p>Doing a large graphic design project like a book works a lot like web dev or game programming where it sort of has two levels. First you set up your &ldquo;framework&rdquo; or &ldquo;engine&rdquo;. On the web, this is your web framework and all of your CSS and HTML templates. In games, it&rsquo;s your game engine. Then you pour content into that structure. With the right framework, adding content is easy.</p> <p>For graphic design using a program like Adobe InDesign, the way it works is you set up styles and masters. A master defines the margins and grid lines for a page. It&rsquo;s the fences that corral all the text to keep those feral letters from running around eating all the whitespace. Styles are like CSS: they let you take a semantic kind of text or object and associate specific fonts, styles and colors for it.</p> <p>In theory, you get the masters and styles right and then typesetting is pretty easy and mechanical. Now, I did <em>not</em> make my life easy when it comes to book design. Book design is literally a two-dimensional spatial exercise and I made my job harder both horizontally and vertically. If you&rsquo;ve read any of it on the web, you know my book has:</p> <ul> <li> <p>Prose, of course. So many words.</p> </li> <li> <p>A lot of asides that need to be right next to certain pieces of text, code or illustrations that they refer to. Some of these can get pretty long.</p> </li> <li> <p>Plenty of code. Also, each code snippet has a little location blurb next to it telling you where the code goes in the resulting program.</p> </li> </ul> <h2 id="how-wide"><a href="#how-wide">How wide?<span class="anchor">#how-wide</span></a></h2> <p>Horizontally, the main text column needs to be wide enough to fit the longest line of code. I can use shorter lines and wrap the snippets more, but that makes them harder to read. It also makes them vertically taller, which causes its own problem.</p> <p>Then I need room next to that for the asides, since they often remark directly on specific sentences. I can make <em>those</em> narrower too, of course. But then they wrap and get taller. Some asides are fairly long and if they get too tall, they start colliding with each other or overlapping location snippets.</p> <p>Oh, and since I ended up writing a 200k+ word book, it&rsquo;s going to have a high page count. That means a thick book. Thick books need wider inner margins so the text doesn&rsquo;t disappear into the spine.</p> <p>All of this points towards a pretty wide page. Most CS textbooks&mdash;at least the ones on my bookshelf&mdash;are 7.5 inches wide. I tried hard to come up with a design that fit the code, asides, and healthy margins in that width while still giving a text size that didn&rsquo;t require a magnifying glass. Eventually, I conceded defeat.</p> <p>Once I tried designing a set of metrics for an 8 inch wide page, everything fell into place. I could have enough breathing room around the text to make it enjoyable to read, a decent length for the code snippets, and plenty of room for the asides. (Using a narrower font for the asides helped too).</p><figure> <img class="framed" src="/image/2021/07/metrics.png"> <figcaption>Here are the final horizontal metrics.</figcaption> </figure> <h2 id="how-tall"><a href="#how-tall">How tall?<span class="anchor">#how-tall</span></a></h2> <p>That left the other dimension. If I were going with a real publisher doing a full offset print run, I could pick whatever page size I wanted. But since I&rsquo;m self publishing, that would mean paying up front for thousands of copies to be printed and, I don&rsquo;t know, storing all the boxes in my garage or something.</p> <p>Print on demand worked great for my last book, and I planned to do the same thing for this one. That meant sticking to the limited set of page sizes that KDP and IngramSpark support. The only reasonable one that is 8 inches wide is 8&rdquo;×10&rdquo;, so that&rsquo;s what I picked. The end result is a book that feels big, but hopefully not awkwardly huge. I&rsquo;m sorry. I promise to write a smaller book if I ever write another.</p><figure> <img class="framed" src="/image/2021/07/master.png"> <figcaption>The metrics for a complete chapter spread. Vertically, text is aligned to a classic 12pt baseline grid.</figcaption> </figure> <p>The whole time I was picking these margins and metrics, I was also selecting fonts and building styles. You can&rsquo;t do graphic design in a vacuum, so I picked a test chapter and just typeset it and tweaked the fonts over and over again. Eventually I got a set of fonts and styles I liked, some masters that seemed workable, and I was ready to go. The framework was done and now it was time to open the content hose.</p> <h2 id="xml-like-its-1999"><a href="#xml-like-its-1999">XML, like its 1999<span class="anchor">#xml-like-its-1999</span></a></h2> <p>Of course, problem #1 is that there <em>is</em> no content hose. I have to build one. InDesign doesn&rsquo;t know what the hell Markdown or my weird ass build system is. I sure as hell didn&rsquo;t want to, like, copy every chapter from my browser, paste it into InDesign and then manually apply all the styles. I have a high pain tolerance, but I&rsquo;m not a masochist.</p> <p>What InDesign <em>can</em> do is import XML. Even better, you can set it up to automatically apply certain paragraph or character styles to certain tag names. InDesign&rsquo;s XML support, alas, has not matured much since the time I described it as <a href="/2014/11/03/bringing-my-web-book-to-print-and-ebook/">being implemented by a narcoleptic intern</a>. For example, in HTML you can italicize a word in a header by taking an italics tag and nesting it inside a header tag. InDesign cannot comprehend such advanced data modeling. It needs a flat series of unnested tags and if you need italics in your header, you damn well better have a unique <code>&lt;italics-header&gt;</code> tag for it.</p> <p>But now I had an ace up my sleeve. Since I had microscopic control over my build system and its Markdown processing, I could write my own <a href="https://github.com/munificent/craftinginterpreters/blob/master/tool/bin/build_xml.dart">custom XML exporter</a> that generated <em>exactly</em> the tags that would make InDesign not cry and avoid as many InDesign XML import bugs as possible.</p> <h2 id="javascript-in-my-indesign"><a href="#javascript-in-my-indesign">JavaScript, in <em>my</em> InDesign?<span class="anchor">#javascript-in-my-indesign</span></a></h2> <p>Even so, XML import only gets you so far. Specifically, it gets you a &ldquo;story&rdquo; in InDesign terms: a single continuous narrative of text that fills the main text box and spans multiple pages:</p><figure> <img class="framed" src="/image/2021/07/convert-before.png"> <figcaption>What the initial XML import looks like. The pink text is where an illustration goes and the "@" tells me where to anchor the aside.</figcaption> </figure> <p>The main story is where the prose and code snippets go, but the asides and the location markers need to be pulled out float off to the side. With my last book, I yanked those out manually. I literally cut each aside from the main text column and pasted it into a new text box. It took forever, and that book was less than half as long as this one with <em>way</em> fewer code snippets.</p> <p>There are <em>1,133</em> code snippets in <em>Crafting Interpreters</em>. If I had to manually cut and paste the location markers for each of those, I would lose my mind. Worse, I would make a lot of mistakes, which would just create more work for myself.</p> <p>I&rsquo;m an engineer so I am morally opposed to error-prone manual effort that can be automated. Did you know that InDesign can be scripted using JavaScript? Well, you do now, which apparently places you in a tiny minority because there are, like, <em>no</em> docs for it out there. You can find a couple of auto-generated references, a few sad cries for help from graphic designers clearly out of their element with no responses, and that&rsquo;s it.</p> <p>JavaScripting InDesign is a special kind of pain. There is no debugger. There are no stack traces. There aren&rsquo;t even <em>debug prints</em>. There is literally just <code>alert()</code>, and you can only call it <em>once</em>. And it halts your script. Fortunately, I actually learned JavaScript back when that&rsquo;s all <em>browsers</em> gave you, so I can hack it.</p><figure> <img class="framed" src="/image/2021/07/debug-js.png"> <figcaption>Just like developing for IE6.</figcaption> </figure> <p>I managed to cobble together a horrific script that would find all of the asides and location markers, pull them out of the main text flow, and leave them off to the side:</p><figure> <img class="framed" src="/image/2021/07/convert-after.png"> <figcaption>Asides and location markers are pulled out of the main flow and into their own text boxes.</figcaption> </figure> <p>What I wasn&rsquo;t able to get the script to do was <em>position</em> the boxes correctly. But InDesign has a thing called &ldquo;anchors&rdquo; where you can lock the position of an element relative to another. A couple of carefully crafted Object Styles would even set the horizontal metrics correctly and align the text to the right baseline. All I had to do was anchor each text box and it worked perfectly!</p> <p>Wait, did I say &ldquo;perfectly&rdquo;? I meant it worked right about half the time and the other half the time InDesign would inexplicably <em>turn off the fucking borders of nearby code snippets</em>.</p><figure> <img class="framed" src="/image/2021/07/anchor.gif"> <figcaption>What it looks like when InDesign hates you.</figcaption> </figure> <p>I lost hours of my life to this stupid bug. Eventually, I realized that some completely random subset of location tags would have to be manually positioned because anchoring invariably broke some borders.</p> <h2 id="editing-again"><a href="#editing-again">Editing, again<span class="anchor">#editing-again</span></a></h2> <p>All of the above took me a good month or so and then it was time to stop fiddling around and get to work on the actual content. The first thing I did was another editing pass of the entire book, front to back. I had already done three drafts of each chapter as I was writing them, but I wanted to do one more now that it was done so that I could get a better feel for continuity. It turns out I repeated the same dumb jokes a lot. I fixed (most of) those.</p> <p>This took five months. There&rsquo;s nothing fun to say about this, it was just a chore.</p> <h2 id="copy-editing"><a href="#copy-editing">Copy editing<span class="anchor">#copy-editing</span></a></h2> <p>Next I hired an actual professional copy editor, <a href="https://karisomerton.com/">Kari Somerton</a>, to go through and do the same thing. She was great. Most of the editing world uses Microsoft Word and &ldquo;Track Changes&rdquo; to handle the editing process. Like most software engineers, I live and breathe plaintext and Git. That way I can see diffs of the changes, and go back through history.</p> <p>I didn&rsquo;t want to abandon my workflow so I asked Kari to ramp up on Git and my weird completely bespoke build system. She handled it with aplomb and churned through the book in no time. She found hundreds and hundreds of mistakes. This despite me doing four drafts and readers filing hundreds of issues already. Professional copy editors are worth every penny.</p> <h2 id="typesetting-the-whole-thing"><a href="#typesetting-the-whole-thing">Typesetting the whole thing<span class="anchor">#typesetting-the-whole-thing</span></a></h2> <p>Once the words were as good as they were gonna get, it was time to get them onto pages. The process went like this:</p> <ol> <li> <p>Create a new InDesign file for the next chapter.</p> </li> <li> <p>Export it to XML.</p> </li> <li> <p>Import the XML into InDesign.</p> </li> <li> <p>Run my little JavaScript script to pull out the asides and location markers.</p> </li> <li> <p>Go through and anchor the side bar stuff.</p> </li> <li> <p>Fix up whitespace at the end of pages.</p> </li> </ol> <p>The first five steps are a piece of cake. I&rsquo;d wake up in the morning, brew a cup of coffee, shamble upstairs to the iMac, and get started. I could grind through those steps while half awake and get a chapter done in half an hour or so. It was peaceful. Almost meditative.</p> <p>And then step six. You see, that right there is the hard thing about typesetting a book. And it&rsquo;s the really hard thing about typesetting <em>this</em> book. Because it turns out there are a bunch of constraints on how content can be fit vertically in a page. Obviously, we can&rsquo;t slice an illustration in half and put the top half on one page and the bottom half on the next. The asides also really need to fit on one page too, or it gets confusing to keep track of what they refer to.</p> <p>Whenever possible, it&rsquo;s nice for the code snippets to not get split across pages too. Some of those can be over a dozen lines long. (This is another reason why wider horizontal metrics helped. Because if I had made the code snippets narrower, they would end up taller, which would make them harder to fit on the page.) And you don&rsquo;t want a header alone at the end of a page with no content after it. And it&rsquo;s good to avoid <a href="https://en.wikipedia.org/wiki/Widows_and_orphans">widows and orphans</a>&hellip;</p> <p>Take all of those rules and restrictions, and mix in the completely fixed height of a page and you got yourself a real constraint solving problem. Or, in my case, 640 of them, all interwoven with each other. Because, you see, InDesign is happy to solve all of this for you by just pushing content to later pages. Code snippet too long? Move it to the next page. No room for prose under the header? Move it all to the next page. What that gives you is a ton of dead white space at the bottom of pages. It looks terrible and wastes space, like this:</p><figure> <img class="framed" src="/image/2021/07/typeset.png"> <figcaption>I thoughtfully left room on the page for your own doodles.</figcaption> </figure> <h2 id="speaking-of-illustrations"><a href="#speaking-of-illustrations">Speaking of illustrations&hellip;<span class="anchor">#speaking-of-illustrations</span></a></h2> <p>At one level, the illustrations were easy. I specifically chose black and white pen and ink because it&rsquo;s print friendly. When I first scanned the images as I wrote each chapter, I brought them in at glorious 1200 DPI. Here&rsquo;s a crop:</p><figure> <img class="framed" src="/image/2021/07/scan.png"> </figure> <p>A little level adjustment in Photoshop produces:</p><figure> <img class="framed" src="/image/2021/07/illustration.png"> </figure> <p>So detailed! Exporting these to high resolution bitmaps that print well was a snap, and they look great. (Well, as great as my handwriting looks, I guess.)</p> <p>Incorporating the illustrations into the page layout was another story. Halfway through typesetting, a lightbulb went off and I finally realized why most books say, &ldquo;Refer to Figure 123 to see blah blah blah&hellip;&rdquo; That gives the typesetter freedom to put Figure 123 wherever the hell it fits on any nearby page.</p> <p>In my dumb book, because I am an idiot, the prose just refers directly to the illustration. The illustration needs to be <em>right there</em> or the text doesn&rsquo;t make sense. I didn&rsquo;t think about that when each chapter was an infinitely scrolling web page, and by the time I realized, it was too late.</p> <p>With the hundreds of illustrations and thousand-plus code snippets, I had given myself thirty giant interrelated bin-packing exercises. The hard part of typesetting was figuring out how to adjust things to minimize that dead space. Sometimes I&rsquo;d split a code snippet in two. Maybe add a little extra padding around one image to spread stuff across the page a little. Or crowd another one so that it <em>just</em> fits on the page. Sometimes I&rsquo;d tweak an illustration to make it shorter to fit on a page or taller to eat up some whitespace.</p> <p>This was the real challenge of typesetting the book and why it took me two months to get through all the chapters.</p> <h2 id="front-matter-and-back-matter"><a href="#front-matter-and-back-matter">Front matter and back matter<span class="anchor">#front-matter-and-back-matter</span></a></h2> <p>Did you know that there are professional indexers? People whose job it is to write indexes for books? They even <a href="https://press.uchicago.edu/ucp/books/book/chicago/I/bo3625262.html">write books</a> about how to write indexes. (One would presume that these books have truly superb indexes.)</p> <p>I did not hire one of those eminently skilled professionals. Instead, I spent two weeks going through every damn chapter <em>again</em> doing my best to pretend that I know what I&rsquo;m doing. InDesign&rsquo;s support for indexes is actually pretty nice. You can basically just select some text and say, &ldquo;Make an index entry for this.&rdquo; Then it collects all of those and generates an index for the whole book. But actually <em>adding</em> all of those entries is a mind-numbing chore.</p> <p>The index is the main piece of <em>back matter</em>&mdash;the stuff at the end of a book after its main content. There is also <em>front matter</em>. You&rsquo;ll never guess where that goes. I put together a title page, copyright page, dedication, and acknowledgements. Then I let InDesign generate a table of contents for me.</p> <p>This was a magical moment. At this point, I had a complete book:</p><figure> <img class="framed" src="/image/2021/07/inside.jpg"> <figcaption>Here's what the inside looks like.</figcaption> </figure> <p>Or, at least, I had the <em>inside</em> of one.</p> <h2 id="cover-design"><a href="#cover-design">Cover design<span class="anchor">#cover-design</span></a></h2> <p>A lot of authors obsess over their cover and envision it the whole time they are writing. Despite what the aphorism says, people <em>do</em> judge books by their cover and a good one makes a big difference. At least, that&rsquo;s the case over in fiction land. In computer science, judging by the other books I have laying around, the artistic merit of the cover appears to be somewhat less critical. I guess when the prof says you have to buy the book to pass the class, a clip art cover is sufficiently compelling.</p> <p>Since I am <em>not</em> a professor who can garner sales by fiat, I spent a lot of time on the cover design. I take photos, so I thought it could look nice to put something detailed on the cover to liven it up. I went through my thousands of photos trying to find something that fit. And, while I have some pretty pictures, none of them felt like they worked as covers. They felt arbitrary.</p> <p>Eventually I realized that the visual language of the book is those pen and ink illustrations. So I drew a bigger more detailed version of <a href="http://craftinginterpreters.com/a-map-of-the-territory.html#the-parts-of-a-language">the mountain illustration I use as a metaphor for the compilation process</a>. I also hand-lettered a new title:</p><figure> <img class="framed" src="/image/2021/07/cover-trace.jpg"> </figure> <p>It&rsquo;s a real typeface (Acumin Pro Extra Condensed) but I hand-traced a printout to give it some imperfect charm. I picked a color palette to try to give it a sort of a mimeographed 1950s scouting manual vibe.</p> <h2 id="proofreading-the-proof"><a href="#proofreading-the-proof">Proofreading the proof<span class="anchor">#proofreading-the-proof</span></a></h2> <p>Now I really had a book. I uploaded the PDF exports to KDP and ordered a proof copy. A week later, a surprisingly heavy box arrived. This was the first moment I really understood just how <em>big</em> this book I wrote is. Up until this point, it was just data files. But seeing it fill up an Amazon box clarified the scale of the project in a way that the time I spent never quite did.</p> <p>So I had a book, but it <em>still</em> wasn&rsquo;t done. Because the typesetting process involved a lot of manual labor. To err is human, so now I had to <em>proofread</em>&mdash;to literally go through the proof and read it looking for mistakes. I marked them all with sticky notes:</p><figure> <img class="framed" src="/image/2021/07/edits.jpg"> <figcaption>I put an "x" on each sticky note to mark when I had applied the fix to the InDesign files.</figcaption> </figure> <p>Here&rsquo;s where it got stressful. If you&rsquo;re a programmer, then source control and diff is deeply ingrained in your workflow. Whenever I make a change, I take for granted that I can then see a diff in the commit to verify that I changed only what I intended <em>and nothing else</em>.</p> <p>I did put the InDesign files in a Git repo, but they are giant opaque binary files. Also, InDesign has a habit of changing them even when it doesn&rsquo;t seem like I&rsquo;ve actually made any real changes. There&rsquo;s nothing quite like syncing all the styles across the chapters, seeing every single file marked changed and wondering, &ldquo;Did I just accidentally move every bullet list item 3 points to the left?&rdquo; It felt like flying blind at exactly the stage in the process where I really wanted to see <em>exactly</em> what was happening.</p> <p>Did I engineer my way out of this problem? You&rsquo;re <a href="https://github.com/munificent/craftinginterpreters/blob/master/tool/bin/tile_pages.dart">damn right I did</a>. I wrote a Dart script that would take a PDF of the book, extract every page, and then generate a single huge PNG file with every page tiled across it. It looks like this:</p><figure class="wide"> <img class="framed" src="/image/2021/07/pages.png"> </figure> <p>If you zoom in, each page is about this big:</p><figure> <img class="framed" src="/image/2021/07/pages-zoom.png"> <figcaption>If you're on a retina display, this is a 100% zoom.</figcaption> </figure> <p>Every time I changed the InDesign files and committed them, I exported a PDF for that commit and generated a tile image. Then I wrote a little Photoshop action that would take two of those and draw a big red border around any pixels that differed. Here is what all of the proofreading changes look like:</p><figure class="wide"> <img class="framed" src="/image/2021/07/diff.png"> </figure> <p>And zoomed in:</p><figure> <img class="framed" src="/image/2021/07/diff-zoom.png"> </figure> <p>There isn&rsquo;t enough detail in the tile image to tell exactly what changed, but the red tells me which pages need a visual inspection. I know I should be proud of, like, writing an entire textbook on programming languages. But, honestly, I think I&rsquo;m most proud of this dumb little script. It was <em>such</em> a relief to be able to programmatically verify that, yes, this PDF looks exactly like the last one except for the one expected change.</p> <h2 id="ebooks-are-also-books"><a href="#ebooks-are-also-books">Ebooks are also books<span class="anchor">#ebooks-are-also-books</span></a></h2> <p>Once I incorporated all the proofreading fixes, the print edition was really for real totally done. But people also like reading ebooks and Kindle, so I needed to make those too.</p> <p>Again, writing my own build system helped. I tweaked it to be able to export the antiquated XHTML that EPUB requires as well as all the weird metadata and manifest stuff that goes into one. A few command-line invocations later and I had Kindle and EPUB e-books. I tested in a bunch of readers and tweaked the CCS to try to find a compromise between all of their renderers.</p> <h2 id="updating-the-site-and-launching"><a href="#updating-the-site-and-launching">Updating the site and launching<span class="anchor">#updating-the-site-and-launching</span></a></h2> <p>At this point&mdash;and now we&rsquo;re talking this past weekend as I write this sentence&mdash;I had a folder with all of the final files for every edition of the book. All that remained was to update the front page of the book&rsquo;s website to point to where people can get them. I took some photos and put on my web designer hat for a while. I tried to make it tolerably responsive.</p> <p>Then I wrote this blog post. It&rsquo;s weird to put that in the past tense since I just wrote it. But I guess it&rsquo;s in the past now.</p> <p>Tomorrow, I&rsquo;m going to upload the files to the various sites and stores. I&rsquo;ll update the site to have links to all of those as they go live. Once the stores have processed the uploads and everything is available, I&rsquo;ll publish this post and update the site. I&rsquo;ll write a note for the mailing list and feel nervous emailing that many people. If you&rsquo;re reading this, I already did all that and the book is really, really done.</p> <h2 id="what-next"><a href="#what-next">What next?<span class="anchor">#what-next</span></a></h2> <p>Ever since I finished the last chapter, people have been asking me what I&rsquo;m going to do next. I have people I consider close friends now who have never known me when I <em>wasn&rsquo;t</em> writing this book.</p> <p>Many ask what I&rsquo;m going to write next, or suggest a topic. I interpret this as a compliment&mdash;they like my writing and want more. But it also feels like asking a mom in labor whether she plans to have more kids. I&rsquo;ve had my legs in the stirrups for six years, so I&rsquo;m gonna just relax a bit after all the pushing is over before I even <em>think</em> about another book baby.</p> <p>What I really plan to do is&hellip; not <em>plan</em> to do anything. This writing project has been an enormous exercise in self-applied delayed gratification. On top of that, the pandemic brought its own basket of deferred dreams. I&rsquo;ve gotten so skilled at denying myself and postponing, so good at tuning out what I <em>feel</em> like doing, that it&rsquo;s hard to even hear those feelings any more. I don&rsquo;t remember what my own joy sounds like.</p> <p>So I&rsquo;m gonna check out for a while and go hunting for my pleasure centers. Maybe <a href="https://www.youtube.com/channel/UCSMJ0iRwAhIFYSpntOEtn2g">make some more music</a>. Maybe go fishing. Probably spend more time with friends and family (safely). I might get back to working on <a href="https://github.com/munificent/hauberk">my roguelike</a>. Or maybe I won&rsquo;t do any of those. Maybe I&rsquo;ll just go out in the backyard and bask mindlessly in the sun like a lizard. The important part is I won&rsquo;t decide until I feel like it.</p> <p>I&rsquo;m sure eventually I&rsquo;ll get the itch to work on something more sizeable again. (But, God-willing, I will never spend six years on a single project again in my life.) Until then, I hope you enjoy the book. Despite all the work, I had a lot of fun making it, and it was <em>always</em> a joy to hear from readers who liked it. It&rsquo;s the only thing that kept me going this long and enabled me to get it done.</p> Crafting “Crafting Interpreters” http://journal.stuffwithstuff.com/2020/04/05/crafting-crafting-interpreters/ Sun, 05 Apr 2020 07:00:00 GMT [email protected] (Robert Nystrom) http://journal.stuffwithstuff.com/2020/04/05/crafting-crafting-interpreters <p>It took three years and 200,000 words more than I expected, but my second book, <em><a href="http://craftinginterpreters.com/">Crafting Interpreters</a></em>, is complete. I finished the third draft of the last chapter today, marking the last of around 1,400 days of continuous writing.</p> <p>This book was <em>much</em> harder than my <a href="http://gameprogrammingpatterns.com/">first book</a>, along every axis. It&rsquo;s larger, more technically complex, much more deeply intertwined, and it had the misfortune of aligning with a really difficult period in my life. Today feels less like coasting past the finish line at the Tour de France, arms raised in triumph, and more like dragging myself onto the beach, clutching sand in relief after a storm-thrashed ordeal at sea.</p> <p>Before I get into all that, I have a minor confession to make. When I finished my first book, I wrote <a href="/2014/04/22/zero-to-95688-how-i-wrote-game-programming-patterns/">a long post</a> about how I cobbled together enough willpower to reach the end of the last page. Everything in there is true, but there is one fact I superstitiously omitted.</p> <p>Halfway through writing <em>Game Programming Patterns</em>, I discovered a new passion: programming languages. It had been a long time since a topic ignited my brain to the same degree, and I was <em>on fire</em>. I spent basically every free hour (and many not-so-free hours&mdash;sorry, family) designing and hacking on programming languages. I read <a href="https://twitter.com/munificentbob/status/901543375945388032">every book I could get my hands on</a>, <a href="/2010/07/23/what-i-learned-at-the-emerging-languages-camp/">went to conferences</a>, <a href="/category/language/">blogged</a>, I even <em>dreamed</em> about programming languages. This infatuation was the main reason I stopped working on my first book for two years.</p> <p>I have a personality quirk where when I&rsquo;m excited about something I just <em>have</em> to teach it to other people. Hermione Granger, arm waving feverishly to get the teacher&rsquo;s attention, is my spirit animal. It was inevitable that I would write something about interpreters. But I couldn&rsquo;t just drop one half-finished book to start another. I have gigs of unfinished projects laying around, but&mdash;maybe because the completed chapters were already online&mdash;I couldn&rsquo;t bear to abandon <em>Game Programming Patterns</em>.</p> <p>So I made a promise to myself. If I finished that book, then I would let myself write a second book on interpreters. In part because of that promise, I <em>did</em> manage to complete the chapters, and then <a href="/2014/11/03/bringing-my-web-book-to-print-and-ebook/">the print and e-book editions</a>. What I thought was merely a hobby and personal goal turned out to be a <a href="/2014/11/20/how-my-book-launch-went/">life-changing experience</a>. My little self-published vanity project has <a href="https://www.amazon.com/dp/0990582906">hundreds of five-star reviews</a>, and has been translated to Korean, Japanese, Chinese, German, and Polish. The book did so much better than I expected that I&rsquo;m still not sure how to process it, beyond feeling immense gratitude to everyone who read it, bought a copy, or cheered me on.</p> <h2 id="the-seed-of-a-book"><a href="#the-seed-of-a-book">The seed of a book<span class="anchor">#the-seed-of-a-book</span></a></h2> <p>Once I finished the print edition of <em>Game Programming Patterns</em>, I took some time off. But it didn&rsquo;t take too long for that itch to write about interpreters to come back. I knew exactly what I was getting into with writing a book now, how hard the grind can be. At first, I just noodled around. I wasn&rsquo;t committed to doing anything. It was more a sort of recreational intellectual exercise. If I <em>were</em> to do a book, what would it look like? You know, <em>hypothetically speaking</em>.</p> <p>The very first note I wrote to myself said:</p> <pre class="highlight language-text">high-level goal: a *small* book that builds a complete, efficient interpreter. instead of a wide text about programming language*s*, it is a single path through the language space. aim for 60k words.</pre> <p>My first book was about 90,000 words, and I didn&rsquo;t want to hike a trail that long again. I also had a meta-goal to make programming languages more approachable, and I figured a short text would help. I had this vision of something you could literally hold in your hand or have open next to your laptop while you followed along.</p> <p>To make a small book, I needed a small language and a small implementation. One of my other side projects was <a href="http://wren.io/">a scripting language named Wren</a>. Wren is written in C, with a simple single-pass bytecode compiler inspired by Lua. Building Wren taught me how much functionality you can pack into a few thousand lines of clean C code.</p> <p>For this hypothetical book, I figured <a href="http://craftinginterpreters.com/a-bytecode-virtual-machine.html">a bytecode VM in C</a> like that would be a great fit. It would also give me the chance to cover a bunch of really fun topics like stack-based VMs, object representation, and garbage collection. But Wren wasn&rsquo;t the right language. I like Wren (obviously), but it has some design quirks that I think make it a better language for <em>users</em> but maybe not for teaching. For the book, I wanted a dynamically-typed scripting language in the vein of languages like JavaScript, Python, and Lua.</p> <p>I started tinkering on a new toy language, tentatively named &ldquo;Vox&rdquo;. The goal was to keep things as simple as possible without taking any shortcuts around the hard problems in implementing a language. I wanted a rich expression and statement syntax to cover parsing. First-class functions and closures because they are powerful and challenging to implement efficiently. Classes and methods because that paradigm is so prevalent but omitted by many compiler books.</p> <p>At some point, I realized that dropping readers straight into C was too unfriendly of an introduction. It&rsquo;s hard to teach high-level concepts like parsing and name resolution while also tracking pointers and managing memory. OK, so we&rsquo;ll build <em>two</em> interpreters. First, <a href="http://craftinginterpreters.com/a-tree-walk-interpreter.html">a simple one in a high-level language</a> to focus on concepts. Then a second bytecode VM in C to focus on performance and low-level implementation techniques.</p> <p>Somehow, I didn&rsquo;t notice that maybe this &ldquo;handbook&rdquo; wasn&rsquo;t going to be as pocket-sized as I hoped.</p> <p>My first choice for the high-level implementation language was JavaScript. I implemented most of a Vox interpreter in JS, but never really liked it. I wanted to write the interpreter in an object-oriented style because there are techniques like the <a href="http://craftinginterpreters.com/representing-code.html#the-visitor-pattern">Visitor pattern</a> for doing language stuff in OOP that aren&rsquo;t covered well elsewhere. Doing OOP in JS means deciding whether to use classes or a prototypal style. The former is cleaner but infuriates some segment of readers. The latter is verbose and confusing to those not already steeped in prototypes.</p> <p>Also, I missed static types. People reading code in a book don&rsquo;t get the luxury of seeing the code in a debugger where they can see what values are in various variables. Static type annotations in the code help.</p> <p>So I switched to Java. I don&rsquo;t love Java but it seemed like the least biased choice for a statically typed object-oriented language. I found you can tame a lot of its infamous verbosity by simply not programming in 1990s enterprise Java style. Maybe it&rsquo;s not idiomatic to have public fields, but it&rsquo;s a hell of a lot shorter.</p> <p>In parallel, I started building the bytecode VM in C, porting over bits of Wren&rsquo;s implementation and stripping out the Wren-specific stuff. I spent the spring and summer of 2016 circling between these three pieces&mdash;the design of Vox itself, the Java interpreter, and the C bytecode VM. This was a delightful, satisfying period of time. The three parts played off each other in challenging ways. Sometimes I would change the language to make one interpreter simpler, but find doing so made the other interpreter more complex. Other times I&rsquo;d hit on some trick that made everything get smaller and cleaner.</p> <h2 id="getting-back-on-the-horse"><a href="#getting-back-on-the-horse">Getting back on the horse<span class="anchor">#getting-back-on-the-horse</span></a></h2> <p>I remember the exact moment I committed to writing the book. I was stuck on a tricky language design problem: constructor syntax. I knew I wanted classes, which meant some way to construct instances. Adding a <code>new</code> keyword felt too special-purpose for my minimal language. I like Smalltalk and Ruby&rsquo;s approach of making <code>new</code> be a method on the class object itself, but that requires metaclasses and a lot of other machinery.</p> <p>I was struggling to find a way to add instantiation without making the language much bigger. Then I remembered JavaScript&rsquo;s thing where you can simply invoke a &ldquo;class&rdquo; as if it were a function to create new instances. That has all sorts of weird baggage in JavaScript because everything does in JS, but the concept and syntax were perfect. I already had first-class classes. And I already had closures which meant a function call syntax that could be applied to arbitrary expressions. So &ldquo;constructors&rdquo; just became what you got when you invoked a class.</p> <p>I felt like Vox had gelled, like it <em>was</em> a language now. And my two implementations were coming along well too. I was surprised by how few hacks or ugly corners I ran into. The codebases kind of fell together and the more I tweaked them, the nicer they got. It felt more like I had discovered them than that I had created them. It would be a shame to <em>not</em> write the book and put them out there into the world. They wanted me to.</p> <p>I committed to writing the book, and I restarted my rule of writing every single day.</p> <p>I had a few thousand lines of pretty Java and C code, but how do I turn that into a book that can be read in linear order? Compact codebases tend to be highly intertwined with many cyclic dependencies. I didn&rsquo;t want readers to have to slog through ten chapters before they could even run <code>main()</code>.</p> <p>This was the real technical challenge of writing the book&mdash;how do I take two implementations of the same language, and break them into incremental pieces that I can build up a chapter at a time?</p> <p>I made this problem harder for myself because of the meta-goal I had. One reason I didn&rsquo;t get into languages until later in my career was because I was intimidated by the reputation compilers have as being only for hardcore computer science wizard types. I&rsquo;m a college dropout, so I felt I wasn&rsquo;t smart enough, or at least wasn&rsquo;t educated enough to hack it. Eventually I discovered that those barriers existed only in my mind and that anyone <em>can</em> learn it.</p> <p>My main overarching goal of the book is to pass on that feeling, to get readers to understand there&rsquo;s no magic in there and nothing keeping them out. To nail that conceit, I wanted to include <em>every single line of code</em> used by the interpreters in the book. No parser generators, nothing left as an exercise for the reader. If you type in all of the code in the book, you get two complete, working interpreters. No tricks.</p> <p>So not only did I need to break these two interpreters into chapters, I needed to do it without any cheating. I wanted a hard guarantee that at the end of each chapter, you had a program that you could type in, compile, run, and do something with. I knew I wouldn&rsquo;t be able to verify this manually, so it was time to create some tools.</p> <h2 id="a-bespoke-build-system"><a href="#a-bespoke-build-system">A bespoke build system<span class="anchor">#a-bespoke-build-system</span></a></h2> <p>I <a href="https://github.com/munificent/game-programming-patterns/tree/master/book">wrote my first book in Markdown</a>. I slapped together <a href="https://github.com/munificent/game-programming-patterns/blob/master/script/format.py">a tiny Python script</a> that converts the Markdown to HTML and transcludes the code snippets which are stored in separate C++ files. When I started my second book, I took that script and started growing it. It evolved throughout writing the book, but in the end, here is how it works.</p> <p>All of the code for the interpreters are stored in separate source files. I have a <a href="https://github.com/munificent/craftinginterpreters/tree/master/java/com/craftinginterpreters">Java project</a> that contains the complete Java interpreter that you get by the end of that part of the book. Likewise, there&rsquo;s a <a href="https://github.com/munificent/craftinginterpreters/tree/master/c">C project</a> for the bytecode VM. I can edit and build those in an IDE, run tests, debug them, etc. They&rsquo;re real programs.</p> <p>Meanwhile, the text of the book is <a href="https://github.com/munificent/craftinginterpreters/tree/master/book">authored in Markdown</a>, one file per chapter, just like my first book. To include a snippet of code in the book, I put a tag in the Markdown like this:</p> <pre class="highlight language-text">Which can be any of: ^code is-alpha Once we&#39;ve found an identifier, we scan the rest of it using:</pre> <p>Here, the <code>^code</code> line says &ldquo;look up the snippet named &lsquo;is-alpha&rsquo; and insert it here.&rdquo; When the build script generates the HTML for this chapter, it goes off and hunts through the code for that snippet. Over in the code, special comments delimit snippets. The one included here looks like this:</p> <pre class="highlight language-c"><span class="c">//&gt; Scanning on Demand is-alpha</span> <span class="k">static</span> <span class="t">bool</span> <span class="i">isAlpha</span><span class="p">(</span><span class="t">char</span> <span class="i">c</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="p">(</span><span class="i">c</span> <span class="o">&gt;=</span> <span class="s">&#39;a&#39;</span> <span class="o">&amp;&amp;</span> <span class="i">c</span> <span class="o">&lt;=</span> <span class="s">&#39;z&#39;</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="i">c</span> <span class="o">&gt;=</span> <span class="s">&#39;A&#39;</span> <span class="o">&amp;&amp;</span> <span class="i">c</span> <span class="o">&lt;=</span> <span class="s">&#39;Z&#39;</span><span class="p">)</span> <span class="o">||</span> <span class="i">c</span> <span class="o">==</span> <span class="s">&#39;_&#39;</span><span class="p">;</span> <span class="p">}</span> <span class="c">//&lt; Scanning on Demand is-alpha</span></pre> <p>The <code>//&gt;</code> line begins the snippet and says what chapter the snippet appears in and the name of the snippet. The <code>//&lt;</code> line ends the snippet. Pretty straightforward.</p> <p>This let me build the book, but didn&rsquo;t ensure that the thing I built actually worked. So I wrote a separate script that instead of building the <em>book</em>, builds <em>programs</em>. For each chapter, it collects <em>all</em> of the snippets that appear in that chapter and the previous ones and writes them out to separate source files. In other words, it produces a separate interpreter, one for each chapter, containing only the code that readers have seen so far.</p> <p>I put together a Makefile to build those per-chapter versions of each interpreter to make sure they compiled. Of course, compiling successfully doesn&rsquo;t mean they do anything <em>useful</em>. Writing a single correct interpreter is hard. Writing thirty of them&mdash;there are <a href="http://craftinginterpreters.com/contents.html">thirty chapters</a> in the book&mdash;is much harder.</p> <p>I had already harvested a little <a href="https://github.com/munificent/craftinginterpreters/blob/master/util/test.py">test runner</a> from Wren and ported most of Wren&rsquo;s tests over to be <a href="https://github.com/munificent/craftinginterpreters/tree/master/test">Lox tests</a>. (I changed the name of the language in the book since there was already a language out there named &ldquo;Vox.&rdquo;). I took that test runner and extended it to be able to run the tests on each chapter&rsquo;s version of the interpreters. Of course, the tests don&rsquo;t all pass&mdash;the interpreters aren&rsquo;t complete! So I added metadata to track which tests I expected to pass by which point in the book. With this in place, I could automatically verify that the code that I was showing readers did exactly what I expected.</p> <h3 id="more-complex-snippets"><a href="#more-complex-snippets">More complex snippets<span class="anchor">#more-complex-snippets</span></a></h3> <p>The snippet markers look pretty straightforward, and in many cases they are. But reality tends to get messier and I didn&rsquo;t allow myself to sweep any of that mess under nearby rugs. Some changes don&rsquo;t just <em>add</em> code to the interpreter. I try to minimize it, but often you need to <em>replace</em> some existing code. A few lines of code may appear in chapter 5 and then later get superseded in chapter 9 by something more powerful.</p> <p>Obviously, I can&rsquo;t jam both of those snippets into the same source file and expect it to compile. Remember, the source files that I hand author are themselves valid Java and C programs that I can build and run. If a function contained several versions of its body mixed together, odds are slim that the compiler will like what it sees.</p> <p>So, for any piece of code that later gets replaced&mdash;in other words code that is not part of the very final version of each interpreter&mdash;there is a different snippet syntax:</p> <pre class="highlight language-c"><span class="k">static</span> <span class="t">void</span> <span class="i">concatenate</span><span class="p">()</span> <span class="p">{</span> <span class="c">/* Strings concatenate &lt; Garbage Collection concatenate-peek ObjString* b = AS_STRING(pop()); ObjString* a = AS_STRING(pop()); */</span> <span class="c">//&gt; Garbage Collection concatenate-peek</span> <span class="t">ObjString</span><span class="o">*</span> <span class="i">b</span> <span class="o">=</span> <span class="r">AS_STRING</span><span class="p">(</span><span class="i">peek</span><span class="p">(</span><span class="n">0</span><span class="p">));</span> <span class="t">ObjString</span><span class="o">*</span> <span class="i">a</span> <span class="o">=</span> <span class="r">AS_STRING</span><span class="p">(</span><span class="i">peek</span><span class="p">(</span><span class="n">1</span><span class="p">));</span> <span class="c">//&lt; Garbage Collection concatenate-peek</span> <span class="t">int</span> <span class="i">length</span> <span class="o">=</span> <span class="i">a</span><span class="o">-&gt;</span><span class="i">length</span> <span class="o">+</span> <span class="i">b</span><span class="o">-&gt;</span><span class="i">length</span><span class="p">;</span> <span class="t">char</span><span class="o">*</span> <span class="i">chars</span> <span class="o">=</span> <span class="r">ALLOCATE</span><span class="p">(</span><span class="t">char</span><span class="p">,</span> <span class="i">length</span> <span class="o">+</span> <span class="n">1</span><span class="p">);</span> <span class="i">memcpy</span><span class="p">(</span><span class="i">chars</span><span class="p">,</span> <span class="i">a</span><span class="o">-&gt;</span><span class="i">chars</span><span class="p">,</span> <span class="i">a</span><span class="o">-&gt;</span><span class="i">length</span><span class="p">);</span> <span class="i">memcpy</span><span class="p">(</span><span class="i">chars</span> <span class="o">+</span> <span class="i">a</span><span class="o">-&gt;</span><span class="i">length</span><span class="p">,</span> <span class="i">b</span><span class="o">-&gt;</span><span class="i">chars</span><span class="p">,</span> <span class="i">b</span><span class="o">-&gt;</span><span class="i">length</span><span class="p">);</span> <span class="i">chars</span><span class="p">[</span><span class="i">length</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#39;\0&#39;</span><span class="p">;</span> <span class="p">}</span></pre> <p>This block comment contains a snippet of code. The header indicates that this snippet is named &ldquo;concatenate&rdquo; and first appears in the &ldquo;Strings&rdquo; chapter. Then, later, it gets removed when the &ldquo;concatenate-peek&rdquo; snippet in the &ldquo;Garbage Collection&rdquo; chapter appears. In other words, that latter snippet replaces the previous two lines.</p> <p>By storing the code for this snippet inside a block comment, I ensure that the code as it is in the raw source file is still valid. In some places where the interpreter gets revised multiple times, the code can get pretty complex. Here is the <code>main()</code> function of the bytecode VM:</p> <pre class="highlight language-c"><span class="t">int</span> <span class="i">main</span><span class="p">(</span><span class="t">int</span> <span class="i">argc</span><span class="p">,</span> <span class="k">const</span> <span class="t">char</span><span class="o">*</span> <span class="i">argv</span><span class="p">[])</span> <span class="p">{</span> <span class="c">//&gt; A Virtual Machine main-init-vm</span> <span class="i">initVM</span><span class="p">();</span> <span class="c">//&lt; A Virtual Machine main-init-vm</span> <span class="c">/* Chunks of Bytecode main-chunk &lt; Scanning on Demand args Chunk chunk; initChunk(&amp;chunk); */</span> <span class="c">/* Chunks of Bytecode main-constant &lt; Scanning on Demand args int constant = addConstant(&amp;chunk, 1.2); */</span> <span class="c">/* Chunks of Bytecode main-constant &lt; Chunks of Bytecode main-chunk-line writeChunk(&amp;chunk, OP_CONSTANT); writeChunk(&amp;chunk, constant); */</span> <span class="c">/* Chunks of Bytecode main-chunk-line &lt; Scanning on Demand args writeChunk(&amp;chunk, OP_CONSTANT, 123); writeChunk(&amp;chunk, constant, 123); */</span> <span class="c">/* A Virtual Machine main-chunk &lt; Scanning on Demand args constant = addConstant(&amp;chunk, 3.4); writeChunk(&amp;chunk, OP_CONSTANT, 123); writeChunk(&amp;chunk, constant, 123); writeChunk(&amp;chunk, OP_ADD, 123); constant = addConstant(&amp;chunk, 5.6); writeChunk(&amp;chunk, OP_CONSTANT, 123); writeChunk(&amp;chunk, constant, 123); writeChunk(&amp;chunk, OP_DIVIDE, 123); */</span> <span class="c">/* A Virtual Machine main-negate &lt; Scanning on Demand args writeChunk(&amp;chunk, OP_NEGATE, 123); */</span> <span class="c">/* Chunks of Bytecode main-chunk &lt; Chunks of Bytecode main-chunk-line writeChunk(&amp;chunk, OP_RETURN); */</span> <span class="c">/* Chunks of Bytecode main-chunk-line &lt; Scanning on Demand args writeChunk(&amp;chunk, OP_RETURN, 123); */</span> <span class="c">/* Chunks of Bytecode main-disassemble-chunk &lt; Scanning on Demand args disassembleChunk(&amp;chunk, &quot;test chunk&quot;); */</span> <span class="c">/* A Virtual Machine main-interpret &lt; Scanning on Demand args interpret(&amp;chunk); */</span> <span class="c">//&gt; Scanning on Demand args</span> <span class="k">if</span> <span class="p">(</span><span class="i">argc</span> <span class="o">==</span> <span class="n">1</span><span class="p">)</span> <span class="p">{</span> <span class="i">repl</span><span class="p">();</span> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="i">argc</span> <span class="o">==</span> <span class="n">2</span><span class="p">)</span> <span class="p">{</span> <span class="i">runFile</span><span class="p">(</span><span class="i">argv</span><span class="p">[</span><span class="n">1</span><span class="p">]);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="i">fprintf</span><span class="p">(</span><span class="i">stderr</span><span class="p">,</span> <span class="s">&quot;Usage: clox [path]</span><span class="e">\n</span><span class="s">&quot;</span><span class="p">);</span> <span class="i">exit</span><span class="p">(</span><span class="n">64</span><span class="p">);</span> <span class="p">}</span> <span class="i">freeVM</span><span class="p">();</span> <span class="c">//&lt; Scanning on Demand args</span> <span class="c">/* A Virtual Machine main-free-vm &lt; Scanning on Demand args freeVM(); */</span> <span class="c">/* Chunks of Bytecode main-chunk &lt; Scanning on Demand args freeChunk(&amp;chunk); */</span> <span class="k">return</span> <span class="n">0</span><span class="p">;</span> <span class="p">}</span></pre> <p>Maintaining this is not super fun. But, thankfully, I have a build and test system to tell me when I break something.</p> <h2 id="slicing-up-the-interpreters"><a href="#slicing-up-the-interpreters">Slicing up the interpreters<span class="anchor">#slicing-up-the-interpreters</span></a></h2> <p>So I had a tool that could let me split the interpreters across the chapters. If it was possible to break these interpreters into chapters at all, it would let me do so. Now I just had to figure out where to carve the seams. This was the most technically challenging part of the book writing process. I wasn&rsquo;t sure if it was going to work at all.</p> <p>I spent several weeks sketching out potential lists of chapters, sprinkling snippet markers throughout the code, and seeing if the result built. I&rsquo;d get a compile error because a snippet in an early chapter tried to call a function in some later chapter and I would have to go back and reorganize things. I hand-drew dependency graphs between language features and tried to untangle them.</p> <p>Here&rsquo;s an example of how this process unfolded:</p> <ol> <li> <p>To teach functions I want show that recursion works.</p> </li> <li> <p>But to have recursive functions I need control flow. Otherwise, every recursive function recurses infinitely without a base case. So control flow has to come before functions.</p> </li> <li> <p>For control flow, I need side effects so that I can show that a certain code path is <em>not</em> taken. The obvious way to do side effects is to have a <code>print()</code> function that displays output.</p> </li> <li> <p>But I don&rsquo;t have functions yet. That&rsquo;s a cycle. Crap.</p> </li> </ol> <p>Sometimes I had to change the language itself to break cycles. The above example is why Lox has a built in print <em>statement</em> instead of a print <em>function</em>. Because that way we can introduce the print statement before control flow, which is in turn before functions.</p> <p>I had to break a couple of cycles like that but, eventually, to my surprise, I got it all sorted out. I had a complete list of chapters for both interpreters. Every line of code was sorted into a snippet that belonged to one of those chapters. I could build and run each chapter&rsquo;s code. Best of all, each chapter had a reasonably coherent concept and a roughly similar amount of code.</p> <p>Before, I felt like I had a language and code that wanted to get out there into the world. Now I felt like I had a book. Or, at least, I had all of the <em>code</em> for a book.</p> <h2 id="a-chapter-at-a-time"><a href="#a-chapter-at-a-time">A chapter at a time<span class="anchor">#a-chapter-at-a-time</span></a></h2> <p>I wrote my first book one chapter at a time. I drafted, edited, illustrated each chapter and put it online before moving to the next one. Serial publishing for the digital age. I really loved that process. It helped build an audience for the book and gave me incremental feedback which made the book better and kept me going. I don&rsquo;t think I could write a whole book in the dark.</p> <p>I intended to publish this book the same way, but the deeply interconnected nature of the chapters made that much harder. I didn&rsquo;t want to discover a problem with the code in chapter 28 that forced me to tweak things in an earlier chapter that readers had already read. I didn&rsquo;t want to paint myself into a corner or invalidate any previously-published material.</p> <p>So the entire time I was designing the language, coding the interpreters, and splitting the codebases into chapters, I had not done any actual writing. I didn&rsquo;t want to put down any prose until I knew the code was solid. So I spent the summer of 2016 just hacking on code. It was, honestly, a blast. The programming part is definitely the fun part, and it was a joy to tinker on the code and figure out how to break it into chapters. Sort of like making a jigsaw puzzle and solving it at the same time.</p> <p>After a few months, it was all there. Every single line of code for the entire book. A complete list of chapters. And I hadn&rsquo;t written a single word of prose. In theory, &ldquo;all&rdquo; that remained was writing some text to explain the code I had already written along with some pictures. But, for me at least, English is a much more taxing language to write than C or Java. I had all of the difficult work ahead of me, and all of the fun was done.</p> <h2 id="illustrating-by-hand"><a href="#illustrating-by-hand">Illustrating by hand<span class="anchor">#illustrating-by-hand</span></a></h2> <p>Well, not all of the fun. I did still have the illustrations to do. With my last book, I hand-drew little sketchy diagrams to show various bits of architecture. I wanted even more illustrations for this book to make the concepts less abstract, less opaque. Unlike a videogame, you can&rsquo;t <em>see</em> a garbage collector doing its thing. Visual metaphors really help.</p> <p>I liked the hand-drawn look. It furthered my meta-goal of making the material more approachable, more human. But I wanted to up the quality. I wanted them to be more intricate and contain more information. I wanted the drawings to be more detailed. Less like margin doodles and more like, well, <em>illustrations</em>. Maybe even some lowercase letters.</p> <p>The ultimate goal for me is a print book, so I stuck with black and white ink. I wanted a tighter, more &ldquo;spidery&rdquo; style, so I got some technical pens. People often ask me what programs I used for the illustrations, assuming I did them all digitally. Here are the main tools I used:</p><figure> <img class="framed" src="/image/2020/04/tools.jpg"> <figcaption>I went with Pigma Microns in 01 and 005. If I were doing it again, I think I'd do Faber-Castell Pitt pens.</figcaption> </figure> <p>There are two kinds of illustrations in the books: diagram-like ones that show meaningful information, and drawings that are for metaphors or just to be silly jokes. The process is different for each.</p> <p>I draw each diagram in pencil on graph paper. That lets me erase and move things around until I get it where I like:</p><figure> <img class="framed" src="/image/2020/04/pencil.jpg"> <figcaption>All of the vertical and horizontal lines in the illustrations generally fall on the graph paper rules or halfway between them.</figcaption> </figure> <p>Then I tape a piece of tracing paper on top and draw over it in ink:</p><figure> <img class="framed" src="/image/2020/04/ink.jpg"> <figcaption>I make mistakes sometimes, usually when lettering like "upvaluels" here. I fix that in Photoshop after scanning.</figcaption> </figure> <p>I hand letter everything. It takes a <em>long</em> time. I used to do graphic design, and I have this weird tic where any time I see something that looks handwritten, I look for multiple instances of the same letter to see if they are different or if the design just used a handwriting font. It&rsquo;s almost always a handwriting font and I die a little inside to see the illusion evaporate.</p> <p>Well, this is <em>my</em> damned book and no reader will ever feel that disappointment. Every single fucking letter in every one of the illustrations was hand lettered and is unique.</p><figure> <img class="framed" src="/image/2020/04/title.jpg"> <figcaption>Here is the hand-lettered logotype for the book. Each "R" is different!</figcaption> </figure> <p>Also, if that&rsquo;s not obsessive enough, I spent time <em>changing my own handwriting</em> to better match the text font of the book. I taught myself to write double-story &ldquo;a&rdquo; and &ldquo;g&rdquo; letters and practiced by filling pages of paper with the same letter over and over.</p><figure> <img class="framed" src="/image/2020/04/lettering.jpg"> <figcaption>Look at the loop under the "g" in "filling" and the finial on the "a" in "apples".</figcaption> </figure> <p>I also wanted to make sure that the illustrations and text matched each other across the book. To give the text a consistent size, I printed a little height guide:</p><figure> <img class="framed" src="/image/2020/04/metrics.jpg"> <figcaption>The dotted line indicates the x-height. I picked a ratio for that to match the fonts I use for text and code.</figcaption> </figure> <p>I slid this paper under the tracing paper and lettered on top of those lines to keep the metrics the same across the book.</p> <p>To keep the diagram size and line thickness consistent, each illustration has a pair of registration marks a fixed distance apart:</p><figure> <img class="framed" src="/image/2020/04/registration.jpg"> <figcaption>The little marks that the pencils are pointing at.</figcaption> </figure> <p>I scan each illustration into Photoshop for clean up and processing. I use those marks when cropping to ensure that the image maintains the right size relative to other images.</p> <p>I recorded a video of the whole process if you want to see it in action:</p><figure> <iframe width="560" height="315" src="https://www.youtube.com/embed/iN1MsCXkPSA" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> <figcaption>Even in timelapse, it takes a long time.</figcaption> </figure> <p>Writing this all out makes me sound like a crazy person. What the hell am I doing with my life? Or, more importantly, what <em>could I have been doing</em> instead of doing all that?</p> <p>Too late now, I guess. The picture-like drawings have a different workflow since they don&rsquo;t have a lot of straight lines or align to a grid.</p><figure> <img class="framed" src="/image/2020/04/drawing.jpg"> <figcaption>How will readers understand what a stack is without this helpful illustration?</figcaption> </figure> <p>I draw those on regular sketch paper using a non-photo blue pencil. Then I ink on top of that. I scan the paper in RGB and use the blue channel, which mostly makes the blue pencil marks disappear.</p><figure> <img class="framed" src="/image/2020/04/blue.jpg"> <figcaption>The sketch paper bleeds the ink more than I like but I didn't want to change paper partway through the book, so I stuck with it.</figcaption> </figure> <p>It&rsquo;s a lot of work for each image, and this doesn&rsquo;t include all of the work after scanning it. And I wanted a lot of them. By the end, I had this stack of paper:</p><figure> <img class="framed" src="/image/2020/04/stack.jpg"> <figcaption>Such a small image for so much work.</figcaption> </figure> <p>I went through two full pads of tracing paper, two pads of graph paper, a sketch pad, and several pens. I drew 181 illustrations.</p> <h2 id="writing-is-suffering"><a href="#writing-is-suffering">Writing is suffering<span class="anchor">#writing-is-suffering</span></a></h2> <p>I had the code, and I had a process for illustrations. The remaining work was just writing all the words and drawing all the pictures. So that&rsquo;s what I did. I started at chapter one and started writing. For each chapter, I wrote an outline and then a first draft. I did an editing pass over that to fix all the major problems. Then a second pass where I read the whole chapter out loud to fix cadence and other stuff.</p> <p>This is the same process I used for the first book. I stumbled onto something that worked, so I wasn&rsquo;t about to mess it up. I posted each chapter online, and then spent a day fixing bugs that readers noticed. Then I moved on to the next chapter.</p> <p>I wrote. And wrote. And wrote. Every single day. Every now and then I would have a trip or something where I couldn&rsquo;t write. As with my first book, I would bank days by writing multiple sessions per day beforehand and then spend those banked days on days that I didn&rsquo;t write. But for the most part, I wrote every day.</p> <p>In the blog post I wrote after my first book, I whined about how I had to write on days when I traveled for work, on holidays, when the kids had sniffles. At the time, it truly was one of the hardest things I&rsquo;ve ever done.</p> <p>This time was something else entirely. I wrote the day my grandfather died (peacefully, unsurprisingly) and the day my aunt died (tragically, days after retiring). I wrote the day I found out my Mom had cancer and my children saw me cry for the first time. I was flying to Louisiana to keep my Mom company when I turned on my phone during the layover and discovered a dear friend had had a stroke. I wrote that evening. I woke up the next day and found out she had died. I wrote that morning sitting next to my brother in the waiting room of the hospital while my Mom got her PET scan.</p> <p>The morning of my friend&rsquo;s memorial service, I wrote in the hotel. Later that day, I openly sobbed in front of a room full of people. The next day, my wife found out her aunt had terminal cancer. I wrote on the flight home.</p> <p>See that dog up there in my profile photo? That&rsquo;s Ginny. She&rsquo;s on the back cover of my first book. Her myriad health problems finally <a href="https://twitter.com/munificentbob/status/1100898048811491328">caught up with her</a> last spring. People sometimes ask, &ldquo;When did you know you were an adult?&rdquo; For me it was the day I made the call to put my dog down. The hardest part was watching my kids say goodbye to her. I&rsquo;m tearing up now writing about it. I ran my fingers through Ginny&rsquo;s silky fur as the sedatives took her away. I only got through 59 words that afternoon.</p> <p>I wrote the day the US somehow elected a racist, abusive, corrupt demagogue, and every day afterwards as I saw my country and others turn towards hate and authoritarianism. I wrote while climate change and income inequality worsened. And now here I am writing at home on the same desk where I work now, quarantined like most of you all, hoping to survive the worst pandemic the world has seen in a century.</p> <p>This is not about how disciplined I was. Because during what have been some of the worst years of my life, a weird inversion happened. It&rsquo;s not that I was going through that shit and still writing <em>in addition to</em> it. I <em>had</em> to keep writing. Writing was one thing I could still control in the face of many things I could not. If I could make it through the book, maybe I could make it through the other things too. If I had skipped a day it would have meant that the cancer or the deaths beat me that day, that they were stronger than me. I feared what it would mean to me to let go.</p> <p>I got through these four years and kept writing, but I paid a price. When I read the earlier chapters, they have a whimsy and light-heartedness that later chapters lack. We&rsquo;re all going through dark times, and I don&rsquo;t <em>feel</em> light. The past few years left a mark on me, and that mark shows up in the book. I miss the goofier person I used to be, sometimes. But I&rsquo;d like to believe that maybe the person I am now is a little more honest. Maybe some of those jokes were a mask.</p> <p>And, thankfully, Mom is in remission.</p> <p>Psychological self examination aside, I did keep up the writing. Which is good because, <em>man</em> did I underestimate this book. I was aiming for 60,000 words and hoped to get it done in about a year. Here I am four years later sitting on a quarter of a million words.</p> <p>People sometimes ask what it&rsquo;s like writing something that big. I&rsquo;ve been asking myself that for the past couple of weeks. And the weird thing is, <em>I don&rsquo;t know.</em> I&rsquo;ve had my head down for the past four years and haven&rsquo;t looked past the next paragraph or two the entire time. What does it feel like to write an email or draw a picture? Writing the book felt like that. I just happened to do it over and over again. I feel like a marathon runner who&rsquo;s been watching his feet the whole time and didn&rsquo;t even notice when he stumbled over the finish line.</p> <h2 id="now-what"><a href="#now-what">Now what?<span class="anchor">#now-what</span></a></h2> <p><em>Crafting Interpreters</em> is complete now. I had to stop here for a minute and look at that sentence. I&rsquo;ve been working on this book every day for around 1,400 days. I can&rsquo;t <em>wait</em> to take a break. So that&rsquo;s the next step. My plan was to finish the book right before spring break and enjoy a week on the beach with family.</p> <p>That beach trip went the way of so many other plans in early 2020, but I still intend to take a long break. I don&rsquo;t know if you noticed, but we all have a lot of other shit to deal with right now. I&rsquo;m going to relax.</p> <p>Every morning since 2016, I&rsquo;ve woken up with a task I had to do. Until I got my writing done for the day, it was on my mind, weighing me down. Writing left me drained. If you&rsquo;ve ever had a newborn, you know the feeling of always having to carry the baby around. After a while, it&rsquo;s like you forget what it&rsquo;s like to have <em>two</em> free arms. I&rsquo;ve been carrying this baby for four years, so I&rsquo;m looking forward to having both arms for a while.</p> <p>Once I&rsquo;m recharged, the real fun starts. Having the book online is important, but for me, <em>Crafting Interpreters</em> was always meant to be a <em>book</em> with pages and a cover. So after a long bout of editing and bug fixing, I&rsquo;m going to get started doing the page layout for the print edition. I love graphic design, and I can&rsquo;t wait to hold it in my hands.</p> <p>If you&rsquo;d like to hold it in <em>your</em> hands when it comes out, I have <a href="https://mailchi.mp/afd054e73140/robertnystrom">a mailing list</a> where I&rsquo;ll let you know when the book is done. In the meantime, I think I&rsquo;ve earned some rest.</p>