journal.stuffwithstuff.com
http://journal.stuffwithstuff.com/
Programming, languages, compilers, games, etc.en-usSat, 14 Feb 2026 20:01:14 GMTSat, 14 Feb 2026 20:01:14 GMTThe 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’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’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’s sake, I’ll use “AI” here mostly to mean “generative AI”. The stuff
that LLMs and products like ChatGPT do. I think it’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’ll sort it out in my head enough to reach some inner peace. I’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’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’ll point out the myriad ways in which
I’m wrong. That’s a possibility, but I’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 “value”?</p><aside name="net" class="bottom">
<p>I say “at the very least” 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’t untangled any of that stuff in my head yet, so I’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’s useful. If it solves some
material problem in the world or at least creates some joy, that’s value. In
the UX world, they call this <span name="utility">“utility”</span>: the thing a thing actually does.</p><aside name="utility">
<p>In UX, “utility” is used to distinguish from “usability”—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’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’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’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’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 “technology” is <em>anything</em> that enables
humans to more efficiently generate utility. It’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’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’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’re looking for two journey-level developers who care about clean
architecture, thrive in Agile teams, and see modern tooling—including
AI-assisted development—as a way to work smarter, not riskier. You’ll
collaborate with product, architecture, security, and platform engineering
partners to design secure, accessible, scalable applications that replace
decades of legacy complexity.</p>
<p>…</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’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 “protect and
sustain healthy land, air, water, and climate in harmony with a strong economy”.
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’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’s a rectangle of fabric that keeps her neck warm.
The Olympic Peninsula is a lovely corner of the country, but it’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 “no”. As any parent who lovingly clips their kid’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’s a great
scarf but because I chose to spend an irreplaceable fraction of my life making
it. It’s a symbol of how much I care about her.</p>
<p>In short, it has <em>meaning</em>. Meaning doesn’t make an object more useful. It’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’m trying to be clear in my
thinking, so when I talk about objects having meaning… where does that meaning
come from? Why do some objects have more meaning than others?</p>
<p>I’ve spent hundreds of hours knitting over the past couple of years, mostly
making things for friends and family. I’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’t
last forever. Every one of us has a finite amount of <em>things we will ever do.</em>
“Spend time” 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’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’s candle warm our heart?</p>
<p>I believe it’s because we’ve evolved to be a social species and we’re hard-wired
to feel good when we think the tribe wants and values us.</p></aside>
<p>I’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’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’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’s one of my favorite gifts.</p>
<p>I don’t know if we’ll ever get a chance to shoot it. We live on opposite sides
of the country and he can’t handle the gloom of Seattle any more than I can
handle the politics of the South. It’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’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>—another
great pair of filmaking bros—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’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’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—the multiplier that determines how much effort is required to make
an object—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’t want to take the idea that efficiency is a slider between
meaningful-but-scarce and meaningless-plenty too far. It’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’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… I <em>like</em> knitting. It wasn’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’s all
I’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’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—whether that be a loved one, an audience, or humanity as a
whole—then you don’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">“utility music”</span>. I just want a vibe to help me
tune out the world and focus. I can’t even handle lyrics while I’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">“furniture
music”</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
“generative” glommed onto the word “AI”.</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—however tentative—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’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’m only looking at this from the perspective of a
single person’s individual use of AI. The global effects of AI are just as if
not more important. But I haven’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’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’s paranoia around unsigned code
and malware.</p>
<p>Here’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 “File > New > Project…”.</p>
</li>
<li>
<p>Select “macOS”, then under “Application” choose “App”. <em>This step is
important. Choosing “Command Line Tool” will <strong>not</strong> work.</em> Click “Next”.</p>
</li>
<li>
<p>Give your app a name. For “Interface”, choose “XIB”. For “Language”, choose
“Objective-C”. Click “Next”. Choose a place to save your project.</p>
</li>
</ol>
<p>Now you have a vanilla “Hello World” 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’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’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 “Targets” to open the target settings.</p>
</li>
<li>
<p>Under “Frameworks, Libraries, and Embedded Content”, click the “+” to add a
new framework. In the popup that appears, click “Add Other… > Add
Files…”. Navigate to wherever you copied “SDL3.framework” and choose it.</p>
</li>
</ol>
<p>Then let’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 “Hello World!” fill
your screen. Press any key to quit.</p>
<p>Let’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’t figured that out yet.</p>
<p>Now here’s all the wrong stuff I tried first before I got that working…</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 “CLI Command” C app. That got me a “Hello, world!” 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’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’t correctly
signed and safe to use. The OS gave me a popup error with “SDL3.framework Not
Opened” because “macOS cannot verify that this app is free from malware”.</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 “Build Phases”, there is a panel for “Embed
Frameworks”. I dragged <code>SDL3.xcframework</code> under there and checked “Code Sign On
Copy”.</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’ve lost the exact
error message but it looked like one of the tasks was a simple file copy and the
other was a “ProcessXCFramework” invocation. I’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 “Link Binary with Libraries”, checking “Copy only
when installing”, and a bunch of other things I don’t remember. Nothing worked.
Either I removed so much that the compiler couldn’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’t use the entire “SDL3.xcframework”<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’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/SDL3.framework/Versions/A/SDL3
Referenced from: <...> /Users/...
Reason: tried: '/Users/.../Build/Products/Debug/SDL3.framework/
Versions/A/SDL3' (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 “Embed frameworks”). For reasons
that are entirely unclear to me, that worked. I hit Command-R and…</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">'SDL3/SDL.h' file not found</pre>
<p>What? I literally just compiled and ran it. Just to make sure I wasn’t crazy,
I cleaned the build directory and compiled again. That worked! “Hello World!”
popped up on screen again.</p>
<p>Build again… 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 'SDL.h' in framework 'SDL3' (loaded from
'/Users/.../Index.noindex/Build/Products/Debug')</pre>
<p>So what I <em>think</em> is happening is that in the first clean build, it locates the
framework inside my app’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’m working on because those files end up sitting around forever.
So the next thing I did was go under “Project Settings” and changed the
“Derived Data” to be “Project-relative Location”.</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’s fine. In the compiled app, all that’s needed is the library
itself. But for reasons I don’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’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’t find the headers because it’s looking in the wrong place,
maybe I can just tell it where to look. Under “Search Paths”, I added
<code>lib/SDL3.framework/Headers</code> to “Header Search Paths”. (I put the framework
under <code>lib</code> in my project tree, hence the <code>lib/</code> part.) Didn’t help. Tried
making it recursive. Nope.</p>
<p>Maybe just <code>lib/SDL3.framework</code>? No.</p>
<p>I’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’s not a header search path. Instead, I tried adding <code>lib/</code> to
“Framework Search Paths”. No help. Making it recursive didn’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>…which they described only as “It works know after certain changes!”. 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 “CLI Command” instead of an “App”. I’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’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 “App”
instead of “CLI Command”. I set the language to “Objective-C” (because that’s
the “closest” to C, I guess—I’m winging it here). I set “Interface” to “XIB”
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 “Hello World” 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’s source tree. Then in XCode, in the “General” tab of the
main target, under “Frameworks, Libraries, and Embedded Content”, I click the
“+”. From there I choose “Add Other…”, “Add Files…”, 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… it runs! I’ve
got an SDL3 “Hello World!” on screen again.</p>
<p>Now the real test… 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’t have to add SDL3 to my
system framework path. (I didn’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 “CLI Command” instead of an
“App”. 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’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’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’t think to ask her to teach me.</p></aside>
<p>This article exists to get you to do so. Specifically, I’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—for better or worse—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 “knitting”, 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’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’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’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’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’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’t know if I have a good way to explain how much my body craved tactile
experience by the end of that. It’s like my fingers ached. A deep hunger, but
not for taste. I’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—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’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’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’t slide off as easily, but
harder work to push against the friction. Polished stainless steel where the
stitches fly off the needles—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’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’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’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’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’t <em>do</em> anything “out there”. 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’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’t care that
you aren’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 “linear” if there’s only one path from beginning to end, one way to
play. Don’t particularly enjoy the desert level? Tough shit, you gotta get
through it to get to the end. “Nonlinear” games let players choose among
multiple paths to reach the end. “Open world” or “sandbox” 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 “end”.</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’t like socks?
Fine, you don’t ever have to go through a “sock making phase” to graduate into
what you really want to make. Find stranded colorwork too fiddly? There’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’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’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’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’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’re over
the hump. When you first start playing guitar and don’t have the hand strength,
callouses, or dexterity to form chords, it’s <em>really</em> hard. But after a few days
you can get the basics down. Then it’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’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’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’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’s not just the steepness of the curve, but also its height. Some skill curves
top out early. I suspect the world’s greatest kazoo player is not profoundly
better at kazoo than I am. Others seem to have no limit, like the world’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’m talking about knitting and videogames <em>as a metaphor</em>. I don’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’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’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’ll spend an entire evening
working on the drum mixing and at the end I can’t tell if I made things better
or worse. It’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 “I’m making progress”. But it’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’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’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’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’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’t just about
whiling away the hours. It’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’t say it’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’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’m
honest, a little consumption feels kinda nice too.</p>
<h2 id="ok-im-sold"><a href="#ok-im-sold">OK, I’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’t pique your interest, fine. It’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’re probably wondering what
next. Fortunately, there are, like, a million “learn how to knit” 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’s <em>really</em> hard
to convey in book form. You kind of need to watch someone’s hands. Learning from
someone in person is best, but if you don’t have that, YouTube is a pretty good
substitute.</p>
<p>Keep in mind that everyone’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’t worry if what works for them doesn’t work for you. Eventually,
you’ll find one that does.</p><aside name="hands">
<p>I knit “Continental style”. 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’m left-handed, I knit the same way a right-handed person does. I
don’t mirror patterns or make stitches from left to right. If you are sinistral
as well, I recommend still learning right-handed. It doesn’t make that much of a
difference in terms of dexterity, and it’s much easier if you don’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’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’ve used
this many of your fingers doing <em>different</em> things all at once.</p>
<p>I promise that if you’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’ll waste a few
bucks. If you’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’m still tinkering on a <a href="/2023/01/03/type-checking-if-expressions/">scripting language for my hobby fantasy console
project</a>. I’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’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’t interact with other
language features, so I could kick it down the road for now.</p>
<p>That was true until it wasn’t. I’ve been beating my head against the wall around
generics for… oh God I just checked the Git history and it’s three years now.
I still don’t have that pinned down. Parametric types are hard.</p>
<p>Anyway, one of the approaches I’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’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’s a solved problem. But they don’t all solve it
the same way, especially if you dig into some of the more obscure corners of the
language world. Let’s go on a tour…</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’s <code>public</code>, <code>private</code>. Maybe also more
specific ones like <code>protected</code>, and <code>internal</code>.</p>
<p>It’s clear, explicit, and gets the job done. It lets you support a large number
of flavors of access control if you need. It’s also extremely common, so easy
for users coming to a new language to pick up.</p>
<p>The flip side is that it’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’t help. I used to program in C# professionally
for several years and in that time, I can’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’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’ve run into this.</p>
<p>Access control sections mean that you can’t look at a single declaration and
know what its access is. You have to know what section contains the declaration.</p>
<p>There’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’s what Python, Go, and Dart do.</p>
<p>Python’s system is a mixture of informal and language supported. A leading
underscore in a name doesn’t <em>prohibit</em> it from being used outside of the module
but it sends a signal to the user that they <em>shouldn’t</em> use it. Sort of “velvet
rope” 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’s name starts with a capital letter, it’s public.
Otherwise, it’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’s private. Otherwise it’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’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’m highly biased, but Go’s approach has always felt strange to me. I’m sure
it’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’m much more used to Dart’s style. I don’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’re obscure.</strong> If you’re new to Java and you see the word <code>private</code>,
you probably don’t know what it does, but you can guess it has something to
do with “privacy”. With Go, if you see that some names are capitalized and
some aren’t, that tells you absolutely nothing about what’s going on.
Historical baggage? Weird personal preference? Maybe the author is German
and prefers capitalizing nouns?</p>
<p>Likewise, if you’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’s going on because the language’s own syntax
doesn’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’t (in which case
there’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’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’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’m not sure. I don’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’re writing declarations, you don’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’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’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’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">"Hello World!"</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’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’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’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’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’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’t specify anything,
C++ makes your struct members public and your class members private.</p>
<p>It’s hard to beat “zero syntax at all” 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—probably some kind of
construction site hard hat—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’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’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’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’t really object oriented. It’s more procedural.
Sort of “structs plus functions” 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’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’t do Go’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’m not sure that corner of the language will stick, but it’s there right
now.</p>
<p>I could do Python/Dart’s approach and use a leading underscore. But I’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’s not intolerable. But
it feels like an annoying tax. And for a language I’m designing for my own joy,
I’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’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’t read like anything. <code>priv</code> is… strange. I suppose that
<code>pvt</code> is the well-established abbreviation for “private” but that feels a tad
militaristic.</p>
<p>I can’t find any other synonyms for “private” that admit reasonable
abbreviations either. So I’m open to taking this path, but I’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 “private”, like
the declaration is being whispered and can’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’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’t immediately convey what it does, but if you’re just starting
to use the language, you don’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’t think I can literally take Oberon’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’t have one), so it’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">"Hello!"</span><span class="p">)</span>
<span class="k">end</span></pre>
<p>I don’t hate it. But using <code>*</code> feels weird for “private”. If anything, it seems
to emphasize the declaration (which is what it does in Oberon where it means
“public”).</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’t decide if I like or not. Given Python and
Dart, it seems like underscore vaguely conveys “private” 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">"Hello!"</span><span class="p">)</span>
<span class="k">end</span></pre>
<p>It looks odd, but I don’t <em>hate</em> it. Or at least I don’t hate it any more than
any novel programming language syntax usually triggers revulsion.</p>
<p>However, there’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… I could just let it do that. I’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’t
ones that are particularly useful for users anyway.</p>
<p>I keep trying to talk myself out of this approach because it’s so unusual but so
far it seems to be lodged in my head better than any of the alternatives I’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’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’m trying to see
if I can live without subtyping. Since most of my programming experience is in
object-oriented languages, I’ve been learning more about languages that lack—or at least claim to lack—subyping, to see how they work.</p>
<p>The most intriguing one to me is Go because the authors say it doesn’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’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’re reading my blog, you probably already know what subtyping is, but
let’s make sure we’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 “assignment” 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 “I expected a value of <em>this</em> type but you gave me a value of <em>this
other type</em>”.</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’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—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’s another
form of polymorphism that we’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’t without significant costs in terms of language
complexity, which is why I’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
“subtype”, you get zero results. So the answer is a clear “no” 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 “Blava”
that was a literal copy/paste of the Java language specification with every use
of “subtype” replaced with “blubtype”, would you say that Blava has subtyping?
It behaves indistinguishably from a language with subtyping, so I’d be inclined
to say yes.</p>
<p>The Go spec doesn’t mention “subtype”, but it does have a notion of
<a href="https://go.dev/ref/spec#Assignability">“assignability”</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’s methods
are a superset of B’s.</p>
</li>
</ul>
</blockquote>
<p>You know, that sounds an <em>awful lot</em> like subtyping. Is “assignable to” just Rob
Pike’s idiosyncratic way of saying “subtype of”? Does Go have subtyping in
everything except name? Are we just playing semantics? (I mean, we’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’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’s type system were primitives like numbers, structs, and
interfaces then I think you’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’ve been talking about
relations on pairs of types like “is subtype” and “is assignable”. 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’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
“propagate” 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 “how does assignability of slice types <em>vary with respect to
their element types</em>?”. 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’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">"Woof!"</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">"Sparky"</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">"Sparky"</span><span class="p">},</span> <span class="i">Dog</span><span class="p">{</span><span class="s">"Fido"</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’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’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">"Rough (but not ruff)!"</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">"Elm"</span><span class="p">})</span>
<span class="p">}</span></pre>
<p>There’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’s fine. But if
you were to call this and pass in a <code>[]Dog</code>, you’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’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’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’ll consider just functions that don’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’t assignable.</p>
<p>Do we need to be that strict to preserve soundness? Actually, no! Here’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">"Rex"</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’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’s sound and semantically kosher in principle… but Go disallows it:</p>
<pre class="highlight language-text">./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 “co-” prefix means that the subtyping relation between
the inner types goes in the “same direction” as the subtyping relation it
implies about the outer types.</p>
<p>That direction matters because relations like subtyping and assignability aren’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’t go in the same
direction as the outer types? Indeed there are, and they’re right there next to
us. Instead of return types, let’s look at parameter types. Now let’s say we
only care about functions that accept a single parameter and return nothing.
Here’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">"Laika"</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’t as intuitive as return types being covariant, if you
think about it carefully, you’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
“contra-” means “against”.</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’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’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’s way when they want to
do something, so why are function types more restrictive than would be necessary
for soundness?</p>
<p>It’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—types that don’t
contain any other type—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’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’t sound.)</p>
<p>But Go was designed from day one to be a high-performance systems language. It’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’ve only been concerned with how types flow through the type
checker at compile time. But—assuming there are no compile errors—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’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’re
just storing data in memory, Go doesn’t want to make you pay for something you
aren’t using. To that end values of struct types in Go store just the bytes
needed for the struct’s own fields.</p>
<p>If a field of a struct is itself some struct type, the inner struct’s fields are
splatted directly into the surrounding struct’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 “chunkier” 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’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’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—you only pay for the increased memory and indirection cost of this
representation—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’s mostly a
“declaration time” choice. Once you’ve decided something is a class, every
variable of that class’s type will store it as a reference to a heap-allocated
object. Conversely, if you’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 “should I use an interface, pointer, or struct type here?”,
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’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">"Rex"</span><span class="p">}</span>
<span class="p">}</span></pre>
<p>Shouldn’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’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’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’s memory representation to the destination type’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’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’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’s runtime representation
to the destination type’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’s no right place to put it. Putting it inside the
function itself doesn’t work because it might be called with a variety of
different parameter types and we don’t know what to convert it from. Putting it
at the callsite before the parameters are passed likewise wouldn’t work because
we don’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’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’s you
might adopt this perspective. If you didn’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’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’s the way you’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’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’m an active Go user and want to know
what’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, “<em>Why</em> did they design it
this way and does that choice make sense in other languages?” And for this
specific design choice, I think it’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 “lifted” 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’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’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’m working on taking my little videogame
scripting language and turning it into a statically typed one. As much as
possible, I’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’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’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’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’ve ever found yourself building a struct or class and writing a comment that
says “If this field is blah then this other field will be null.” then you’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’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’t help myself anymore.</p>
<p>There are a handful of solutions to the problem. I’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 “null”, “nil”, 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’t</em> heterogeneous and
<em>should</em> always be present. If you make every single reference nullable,
you’ve lost the ability to distinguish ones that can be absent from ones
that really shouldn’t be.</p>
<p>This is why many newer statically typed languages either don’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 “variant” 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’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>—you should only ever have one of them and not the others—then this avoids the memory overhead of storing them all separately.</p>
<p>However, in C, the language itself doesn’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 “unions” 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’re also called
“tagged unions” or “discriminated unions”.)</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’d have abstract methods in <code>Weapon</code>
that are overridden in the subclasses to use them.</p>
<p>It’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’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’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’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’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’s an
obvious approach. But I’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… 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’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’t see steps like: “Produce a new bowl
of batter which is the previous bowl of batter and 2 cups of sugar.” It just
says “Add 2 cups of sugar to the bowl.”</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">></span> <span class="n">1</span> <span class="k">then</span>
<span class="i">print</span><span class="p">(</span><span class="s">"You are out of range."</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"><=</span> <span class="i">damage</span> <span class="k">then</span>
<span class="i">print</span><span class="p">(</span><span class="s">"You kill the monster!"</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">"You wound the monster."</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’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’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’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">>=</span> <span class="i">min</span> <span class="k">and</span> <span class="i">distance</span> <span class="o"><=</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">"You are out of range."</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"><=</span> <span class="i">damage</span> <span class="k">then</span>
<span class="i">print</span><span class="p">(</span><span class="s">"You kill the monster!"</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">"You wound the monster."</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’m still noodling on the syntax.</p>
<p>Now, the code here works. And it’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’s cool.</p>
<p>But it’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
“invert” the code and use pattern matching and destructuring.</p>
<p>I do love pattern matching and destructuring—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’m pushing really
hard on simplicity. If possible, I don’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’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’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’t
put in the list up there because, as far as I can tell, it’s basically a dead
end in the evolutionary history of programming languages.</p>
<p>Some versions of Pascal have a thing called “variant records”. A record in
Pascal is your basic “collection of fields” 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’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’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’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">"Min range %d</span><span class="e">\n</span><span class="s">"</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’t know and doesn’t check it. This is
definitely true in C and I think true in Pascal. (It’s always hard to talk about
Pascal definitively because there’s no “Pascal”, just a huge family of
loosely-related Pascal-ish languages.)</p>
<p>In a memory safe language like mine, I definitely don’t want users to be able
to reinterpret memory. But that’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’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’t need to rely on pattern matching to access the
variant fields. They’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">></span> <span class="n">1</span> <span class="k">or</span>
<span class="i">distance</span> <span class="o"><</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">></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">"You are out of range."</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"><=</span> <span class="i">damage</span> <span class="k">then</span>
<span class="i">print</span><span class="p">(</span><span class="s">"You kill the monster!"</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">"You wound the monster."</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’t access fields of the wrong type. But we don’t need to go all the
way to C’s level of unsafety. Instead, when you access a case-specific field on
a record, if the record’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’ve found this to be really handy in practice.</p>
<p>I haven’t decided if I’m totally sold on this feature yet. But in the
(admittedly small) amount of example code I’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"><</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">></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">></span> <span class="n">1</span> <span class="k">then</span>
<span class="i">print</span><span class="p">(</span><span class="s">"You are out of range."</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’t</em> guard the code with that kind of check, you’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"><</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't access .minRange here.</span>
<span class="i">distance</span> <span class="o">></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">"You are out of range."</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 “control flow analysis”
and the specific feature is called “flow typing”, “smart casts”, or “type
promotion” depending on which language.</p>
<p>Is it a good fit for my language? I do like that it makes imperative code “just
work” while being safe. But that “just” 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’t sound in many cases that users expect to work. Once the variable
that you’re type testing can escape the current function, the compiler generally
can’t prove that it won’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’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’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’t a
subtype of <code>Weapon</code>, it’s a case constructor. The <code>weapon is MeleeWeapon</code> syntax
looks like a type test, but it’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 “known case” 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’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">></span> <span class="n">1</span> <span class="k">then</span>
<span class="i">print</span><span class="p">(</span><span class="s">"You are out of range."</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"><</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">></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’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’m trying to avoid it because I feel like it’s a bigger lump of complexity
than I want to take on.</p>
<p>I’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’ve been hacking on for several years. It’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’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’t know if I’ll ever pull this off or the language will ever see the light of day, but it’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’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">></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"><</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"><</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">></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"><</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">></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">></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"><</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’t need a separate
<code>if</code> statement and conditional expression. Also, I don’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’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’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’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—text editor, music stuff, etc.—using that.</p>
<p>Now, UXN is <em>really</em> minimal. I get a certain satisfaction from programming in
assembly, but it’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’d</em> want to use to make little 2D videogames? The game I’ve
spent the most time hacking on is my also-perennially-incomplete roguelike
<a href="https://github.com/munificent/hauberk">Hauberk</a>. I’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’m refactoring and
the type system guides me to what’s left to clean up. I just really like working
with types. (It’s OK if you don’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’ve been sketching out a statically typed variant of my console’s scripting
language. I don’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’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’ll walk through a bunch of examples and build up to the type checking rules I
have settled on (so far, at least). We’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">"in love"</span> <span class="k">else</span> <span class="s">"not in love"</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’ll start with is: <strong>An <code>if</code> expression’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’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">"in love"</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’s answer</a> is <code>String | Int</code>. Union
types are cool but definitely too complex for the language I’m trying to make.</p>
<p>In Kotlin, which is also typed and expression-oriented, the answer is,
apparently, <code>{Comparable<CapturedType(*)> & 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’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’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’s the next rule: <strong>If the branches have different types, it’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’s basically SML’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">"in love"</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’t know what
type of value the <code>if</code> expression evaluates to.</p>
<p>But it doesn’t <em>matter</em> since the <code>if</code>’s value isn’t being used anyway. There’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’t be
used, then it’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 “value not used” 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">"About to return three..."</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 "About to return three..." then "3".</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’t return a value. In that case, even the last
expression in the body is a “value not used” context.</p>
</li>
<li>
<p>Loop expressions don’t produce values, so their body is always a “value not
used” context. (I’m toying with the idea of allowing <code>break</code> expressions to
yield a value from the loop, but they don’t right now.)</p>
</li>
<li>
<p>Whenever an <code>if</code> or <code>match</code> expression is in a “value not used” 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’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> “as an expression” then the two branches must have the same type. That’s
roughly the distinction I’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’s obviously common to have <code>if</code>s whose main purpose
is a side effect and where an <code>else</code> clause isn’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’s value
isn’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’t
used.</strong></p>
<h2 id="exiting-branches"><a href="#exiting-branches">Exiting branches<span class="anchor">#exiting-branches</span></a></h2>
<p>We’re almost there. It’s starting to feel like we really are type-checking an
imperative language, not ML in BASIC’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’s one last bit of type checking logic for <code>if</code>
expressions. I haven’t decided if it’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">"in love"</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 “in love”.
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’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’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">“bottom”, <code>⊥</code> (“up tack”), <code>Never</code>, <code>noreturn</code>, etc</a>.
This type means “You’re never gonna get a value from me.”</p>
<p>When checking the two branches of an <code>if</code> expression, if one branch has that
special type (the compiler calls it “unreachable” right now), then we just use
the type of the other branch for the <code>if</code> expression’s type. That allows the
above example to work. In the sample code I’ve written so far, it rarely comes
into play. It’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’s where I’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’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 “unreachable” then the <code>if</code> expression’s type is
also “unreachable”.</p>
</li>
<li>
<p>If one branch has type “unreachable” then the <code>if</code> expression’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’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’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’s obviously a terrible idea.
Family members that we’re vacationing with look at us like we are idiots when we
say we’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’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’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’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 “Dorito” because of your triangular orange ears, but it doesn’t stick.
Instead, you become “Ginny”. 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’t respect you as
the pack leader if they are free to sleep where you do. They’ll think they run
the pack.</p>
<p>It’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’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’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 “I am a
puppy and don’t like being alone in this crate” and the yowl that means “My
bladder is too small to last the night and I need to pee right now.” Sorry about
the mistakes we made while figuring that out.</p>
<p>I pee in the yard too, hoping you’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’s not like you speak English, so when I say, “I hate your
face,” all you hear is the caring tone I use to say it. “You’re so fucking
stupid,” 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’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’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’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’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, “If she gets
hungry enough, she’ll eat. It’s not like she’s going to starve herself.”</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’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’s fifty degrees and rainy. I’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… 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’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’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 “beignet” because he’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 “keh keh keh”.
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’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’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’s cold and wet, but you don’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’s why you pee all the time—often in the house—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’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’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’s six o’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 “congestive heart <em>failure</em>” 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 “for now”.</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’t
miss any.</p>
<p>It is exhausting, but mostly for us. You are still relatively content, lazing
around the house. Stealing Benny’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’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’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’t the same. You don’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’s clearly time. I feel an unexpected relief. For years, I have held a deep
fear that I wouldn’t know when it was over. That I wasn’t adult enough to handle
the responsibility of making the call. I’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’s no doubt.</p>
<p>I learn there is an entire category of vet-adjacent businesses that specialize
in this “transition”. 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’t
know the right social protocol to ask, “Can you please come kill my dog?”</p>
<hr />
<p>You are sitting next to me on the couch, pressed warmly against my side like
always. You’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’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 “put to sleep”
figurative sense. As the woman pushes the plunger, you loll slowly over onto my
leg. You’re napping, head on my thigh, more comfortable than I’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’s later that day. I’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’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 – 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’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 “hands” plural.
Because this little “handbook” 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’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’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’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’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’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’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’m showing you
does what it’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’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’s old Liquid templates to that. I didn’t find a good syntax
highlighter. But it’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’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’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
“framework” or “engine”. On the web, this is your web framework and all of your
CSS and HTML templates. In games, it’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’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’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’s going to have a high
page count. That means a thick book. Thick books need wider inner margins so the
text doesn’t disappear into the spine.</p>
<p>All of this points towards a pretty wide page. Most CS textbooks—at least the
ones on my bookshelf—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’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’m
self publishing, that would mean paying up front for thousands of copies to be
printed and, I don’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”×10”, so that’s what I picked. The end result is a book that feels big, but
hopefully not awkwardly huge. I’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’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’t know what the hell Markdown or my weird ass build system is. I
sure as hell didn’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’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’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><italics-header></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 “story” 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’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’s it.</p>
<p>JavaScripting InDesign is a special kind of pain. There is no debugger. There
are no stack traces. There aren’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’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’t able to get the script to do was <em>position</em> the boxes correctly.
But InDesign has a thing called “anchors” 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 “perfectly”? 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’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 “Track Changes” 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’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’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’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’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’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’t want a header alone at the end of a page with no content
after it. And it’s good to avoid <a href="https://en.wikipedia.org/wiki/Widows_and_orphans">widows and orphans</a>…</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…<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’s print friendly. When I first scanned the images as I
wrote each chapter, I brought them in at glorious 1200 DPI. Here’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, “Refer to Figure 123 to see blah blah blah…” 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’t
make sense. I didn’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’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’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’m doing. InDesign’s support for indexes is actually pretty nice.
You can basically just select some text and say, “Make an index entry for this.”
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>—the stuff at the end of a book
after its main content. There is also <em>front matter</em>. You’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’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’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’t done. Because the typesetting process
involved a lot of manual labor. To err is human, so now I had to <em>proofread</em>—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’s where it got stressful. If you’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’t seem
like I’ve actually made any real changes. There’s nothing quite like syncing all
the styles across the chapters, seeing every single file marked changed and
wondering, “Did I just accidentally move every bullet list item 3 points to the
left?” 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’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’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’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—and now we’re talking this past weekend as I write this
sentence—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’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’s weird to put that in the past tense since I
just wrote it. But I guess it’s in the past now.</p>
<p>Tomorrow, I’m going to upload the files to the various sites and stores. I’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’ll publish this post
and update the site. I’ll write a note for the mailing list and feel nervous
emailing that many people. If you’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’m
going to do next. I have people I consider close friends now who have never
known me when I <em>wasn’t</em> writing this book.</p>
<p>Many ask what I’m going to write next, or suggest a topic. I interpret this as a
compliment—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’ve had my legs in the
stirrups for six years, so I’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… 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’ve gotten so
skilled at denying myself and postponing, so good at tuning out what I <em>feel</em>
like doing, that it’s hard to even hear those feelings any more. I don’t
remember what my own joy sounds like.</p>
<p>So I’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’t do any of those. Maybe I’ll just go out in
the backyard and bask mindlessly in the sun like a lizard. The important part is
I won’t decide until I feel like it.</p>
<p>I’m sure eventually I’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’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’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—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’m excited about something I just <em>have</em>
to teach it to other people. Hermione Granger, arm waving feverishly to get the
teacher’s attention, is my spirit animal. It was inevitable that I would write
something about interpreters. But I couldn’t just drop one half-finished book to
start another. I have gigs of unfinished projects laying around, but—maybe
because the completed chapters were already online—I couldn’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’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’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’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’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’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 “Vox”. 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’s hard to teach high-level concepts like
parsing and name resolution while also tracking pointers and managing memory.
OK, so we’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’t notice that maybe this “handbook” wasn’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’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’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’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’s not idiomatic to have public fields, but it’s a hell of a lot
shorter.</p>
<p>In parallel, I started building the bytecode VM in C, porting over bits of
Wren’s implementation and stripping out the Wren-specific stuff. I spent the
spring and summer of 2016 circling between these three pieces—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’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’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’s thing where you can simply invoke a
“class” 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 “constructors” 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’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—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’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’m a college dropout, so I felt I wasn’t smart enough, or
at least wasn’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’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’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’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’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've found an identifier, we scan the rest of it using:</pre>
<p>Here, the <code>^code</code> line says “look up the snippet named ‘is-alpha’ and insert it
here.” 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">//> 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">>=</span> <span class="s">'a'</span> <span class="o">&&</span> <span class="i">c</span> <span class="o"><=</span> <span class="s">'z'</span><span class="p">)</span> <span class="o">||</span>
<span class="p">(</span><span class="i">c</span> <span class="o">>=</span> <span class="s">'A'</span> <span class="o">&&</span> <span class="i">c</span> <span class="o"><=</span> <span class="s">'Z'</span><span class="p">)</span> <span class="o">||</span>
<span class="i">c</span> <span class="o">==</span> <span class="s">'_'</span><span class="p">;</span>
<span class="p">}</span>
<span class="c">//< Scanning on Demand is-alpha</span></pre>
<p>The <code>//></code> line begins the snippet and says what chapter the snippet appears in
and the name of the snippet. The <code>//<</code> line ends the snippet. Pretty
straightforward.</p>
<p>This let me build the book, but didn’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’t mean they do anything <em>useful</em>. Writing a single correct interpreter is
hard. Writing thirty of them—there are <a href="http://craftinginterpreters.com/contents.html">thirty chapters</a> in the
book—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’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 “Vox.”). I took that
test runner and extended it to be able to run the tests on each chapter’s
version of the interpreters. Of course, the tests don’t all pass—the
interpreters aren’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’t allow myself to sweep any of that mess
under nearby rugs. Some changes don’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’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—in other words code that
is not part of the very final version of each interpreter—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 < Garbage Collection concatenate-peek
ObjString* b = AS_STRING(pop());
ObjString* a = AS_STRING(pop());
*/</span>
<span class="c">//> 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">//< 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">-></span><span class="i">length</span> <span class="o">+</span> <span class="i">b</span><span class="o">-></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">-></span><span class="i">chars</span><span class="p">,</span> <span class="i">a</span><span class="o">-></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">-></span><span class="i">length</span><span class="p">,</span> <span class="i">b</span><span class="o">-></span><span class="i">chars</span><span class="p">,</span> <span class="i">b</span><span class="o">-></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">'\0'</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 “concatenate” and first appears in the “Strings” chapter. Then,
later, it gets removed when the “concatenate-peek” snippet in the “Garbage
Collection” 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">//> A Virtual Machine main-init-vm</span>
<span class="i">initVM</span><span class="p">();</span>
<span class="c">//< A Virtual Machine main-init-vm</span>
<span class="c">/* Chunks of Bytecode main-chunk < Scanning on Demand args
Chunk chunk;
initChunk(&chunk);
*/</span>
<span class="c">/* Chunks of Bytecode main-constant < Scanning on Demand args
int constant = addConstant(&chunk, 1.2);
*/</span>
<span class="c">/* Chunks of Bytecode main-constant < Chunks of Bytecode main-chunk-line
writeChunk(&chunk, OP_CONSTANT);
writeChunk(&chunk, constant);
*/</span>
<span class="c">/* Chunks of Bytecode main-chunk-line < Scanning on Demand args
writeChunk(&chunk, OP_CONSTANT, 123);
writeChunk(&chunk, constant, 123);
*/</span>
<span class="c">/* A Virtual Machine main-chunk < Scanning on Demand args
constant = addConstant(&chunk, 3.4);
writeChunk(&chunk, OP_CONSTANT, 123);
writeChunk(&chunk, constant, 123);
writeChunk(&chunk, OP_ADD, 123);
constant = addConstant(&chunk, 5.6);
writeChunk(&chunk, OP_CONSTANT, 123);
writeChunk(&chunk, constant, 123);
writeChunk(&chunk, OP_DIVIDE, 123);
*/</span>
<span class="c">/* A Virtual Machine main-negate < Scanning on Demand args
writeChunk(&chunk, OP_NEGATE, 123);
*/</span>
<span class="c">/* Chunks of Bytecode main-chunk < Chunks of Bytecode main-chunk-line
writeChunk(&chunk, OP_RETURN);
*/</span>
<span class="c">/* Chunks of Bytecode main-chunk-line < Scanning on Demand args
writeChunk(&chunk, OP_RETURN, 123);
*/</span>
<span class="c">/* Chunks of Bytecode main-disassemble-chunk < Scanning on Demand args
disassembleChunk(&chunk, "test chunk");
*/</span>
<span class="c">/* A Virtual Machine main-interpret < Scanning on Demand args
interpret(&chunk);
*/</span>
<span class="c">//> 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">"Usage: clox [path]</span><span class="e">\n</span><span class="s">"</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">//< Scanning on Demand args</span>
<span class="c">/* A Virtual Machine main-free-vm < Scanning on Demand args
freeVM();
*/</span>
<span class="c">/* Chunks of Bytecode main-chunk < Scanning on Demand args
freeChunk(&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’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’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’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’t have functions yet. That’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’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’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’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’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’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’t written a single word of prose.
In theory, “all” 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’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 “spidery” 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’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’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 “a” and “g” 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’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’s a lot of work for each image, and this doesn’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’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’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’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’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’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’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’s Ginny. She’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, “When did you know you were an adult?” 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’m tearing up now writing about it. I ran my
fingers through Ginny’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’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’re all going through dark times, and I don’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’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’s like writing something that big. I’ve been asking
myself that for the past couple of weeks. And the weird thing is, <em>I don’t
know.</em> I’ve had my head down for the past four years and haven’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’s been watching his feet
the whole time and didn’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’ve been working on this book every day for around 1,400
days. I can’t <em>wait</em> to take a break. So that’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’t know if you noticed, but we all have a lot
of other shit to deal with right now. I’m going to relax.</p>
<p>Every morning since 2016, I’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’ve ever had a newborn, you know the feeling of always having to
carry the baby around. After a while, it’s like you forget what it’s like to
have <em>two</em> free arms. I’ve been carrying this baby for four years, so I’m
looking forward to having both arms for a while.</p>
<p>Once I’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’m going to get
started doing the page layout for the print edition. I love graphic design, and
I can’t wait to hold it in my hands.</p>
<p>If you’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’ll let you know when the book is done. In the meantime, I
think I’ve earned some rest.</p>