<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Programming Languages on Thunderseethe's Devlog</title><link>https://thunderseethe.dev/tags/programming-languages/</link><description>Recent content in Programming Languages on Thunderseethe's Devlog</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Thu, 19 Feb 2026 14:13:55 +0000</lastBuildDate><atom:link href="https://thunderseethe.dev/tags/programming-languages/index.xml" rel="self" type="application/rss+xml"/><item><title>Compiler Education Deserves a Revolution</title><link>https://thunderseethe.dev/posts/compiler-education-deserves-a-revoluation/</link><pubDate>Thu, 19 Feb 2026 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/compiler-education-deserves-a-revoluation/</guid><description>&lt;div class="notice note">
 &lt;div class="notice-title">
 &lt;i class="fa fa-sticky-note" aria-hidden="true">&lt;/i>Note
 &lt;/div>
 &lt;div class="notice-content">I was invited to write an article for the &lt;a 
 href="https://pagedout.institute/"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://pagedout.institute/"
 data-goatcounter-title="Paged Out"
 data-goatcounter-referrer="Compiler Education Deserves a Revolution"
 >
 Paged Out
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
 magazine.
If you would like to see this article in glorious HD color, with some fun diagrams, check out &lt;a 
 href="https://pagedout.institute/webview.php?issue=8&amp;amp;page=1"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://pagedout.institute/webview.php?issue=8&amp;amp;page=1"
 data-goatcounter-title="issue 8"
 data-goatcounter-referrer="Compiler Education Deserves a Revolution"
 >
 issue 8
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
.
I think it&amp;rsquo;s pretty neat.&lt;/div>
&lt;/div>

&lt;p>Crack open any compiler tome from the last century and you&amp;rsquo;ll find some variant of the same architecture.
A pipeline that runs each pass of the compiler over your entire code before shuffling its output along to the next pass.
The pipeline halts at the first error, throwing away any work that&amp;rsquo;s been completed.&lt;/p></description></item><item><title>How to Choose Between Hindley-Milner and Bidirectional Typing</title><link>https://thunderseethe.dev/posts/how-to-choose-between-hm-and-bidir/</link><pubDate>Sun, 15 Feb 2026 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/how-to-choose-between-hm-and-bidir/</guid><description>&lt;p>This question is common enough you&amp;rsquo;ve probably heard it posed countless times:
&amp;ldquo;Should my new programming language use a Hindley-Milner (HM) type system or a Bidirectional (Bidir) one?&amp;rdquo;
What&amp;rsquo;s that?
I need to understand friends don&amp;rsquo;t just bring up type inference in casual conversation?&lt;/p>
&lt;p>OK, ouch, fair enough.
But&amp;hellip;whatever.
This is my blog.
We&amp;rsquo;re doing it anyway!
I don&amp;rsquo;t know what you expected when you clicked on a programming languages blog.&lt;/p></description></item><item><title>Making an LSP for great good</title><link>https://thunderseethe.dev/posts/lsp-base/</link><pubDate>Wed, 21 Jan 2026 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/lsp-base/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This post brings together all our passes to build a language server.
It mainly relies on the output of the frontend passes (parsing, desugar, nameres, and types).&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>We&amp;rsquo;ve talked a big game about resilience this and interactive that.
With &lt;a 
 href="https://thunderseethe.dev/posts/nameres-base"
 
 >
 name resolution
 
&lt;/a>
 done, it&amp;rsquo;s finally time to put our money where our mouth is.
It&amp;rsquo;s time to tie our passes together into a compiler, but not just any compiler.
Sure, we &lt;em>could&lt;/em> string together a batch compiler if we really had to:&lt;/p></description></item><item><title>Resolving Names Once and for All</title><link>https://thunderseethe.dev/posts/nameres-base/</link><pubDate>Sat, 27 Dec 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/nameres-base/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This post covers name resolution.
&lt;a 
 href="https://thunderseethe.dev/posts/desugar-base"
 
 >
 Desugaring
 
&lt;/a>
 left us with an &lt;code>Ast&amp;lt;String&amp;gt;&lt;/code> that we can&amp;rsquo;t yet feed to type inference.
Name resolution will turn that into a &lt;code>Ast&amp;lt;Var&amp;gt;&lt;/code> that is ready for type checking by figuring out what the names in our program mean.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>Why do we name things?
A silly question, perhaps.
No one had to tell me to name my pet rock Francis.
One look at his sedimentary exterior was all it took to christen him.&lt;/p></description></item><item><title>Desugaring the Relationship Between Concrete and Abstract Syntax</title><link>https://thunderseethe.dev/posts/desugar-base/</link><pubDate>Tue, 02 Dec 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/desugar-base/</guid><description>&lt;p>Previously, we, begrudgingly, &lt;a 
 href="https://thunderseethe.dev/posts/parser-base"
 
 >
 parsed some syntax into a Concrete Syntax Tree (CST)
 
&lt;/a>
.
With that tarpit deftly dodged, we can proceed to our next pass desugaring.
Desugaring removes syntax sugar and maps our CST onto our Abstract Syntax Tree (AST).
Our CST leaves us with a lot of cruft, such as &lt;code>|&lt;/code> or &lt;code>=&lt;/code>.
This stuff was important for telling head from tail in our initial source file, and we&amp;rsquo;ll want to have it around when we&amp;rsquo;re reporting diagnostics, but the rest of the compiler doesn&amp;rsquo;t really care about such mundane affairs.
Desugaring helps us strip away all the syntax and focus in on what&amp;rsquo;s important, lightening the cognitive load for following compiler passes.&lt;/p></description></item><item><title>Reproachfully Presenting Resilient Recursive Descent Parsing</title><link>https://thunderseethe.dev/posts/parser-base/</link><pubDate>Wed, 12 Nov 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/parser-base/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This post covers parsing.
Parsing is the first pass of the compiler, so it doesn&amp;rsquo;t depend on anything from the previous posts.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>I must profess a certain disdain for the parsing tutorial.
I won&amp;rsquo;t insult you by pretending it&amp;rsquo;s fair or rational.
This may not come as a surprise, given that the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 Making a Language
 
&lt;/a>
 series has covered &lt;a 
 href="https://thunderseethe.dev/posts/types-base"
 
 >
 every
 
&lt;/a>
 &lt;a 
 href="https://thunderseethe.dev/posts/lowering-base-ir"
 
 >
 part
 
&lt;/a>
 &lt;a 
 href="https://thunderseethe.dev/posts/simplify-base"
 
 >
 of the
 
&lt;/a>
 &lt;a 
 href="https://thunderseethe.dev/posts/closure-convert-base"
 
 >
 compiler
 
&lt;/a>
 &lt;a 
 href="https://thunderseethe.dev/posts/emit-base"
 
 >
 it can
 
&lt;/a>
 before parsing.
Parsing tutorials just feel so futile.
Thousands of tutorials written about parsing, and yet PL projects still die bikeshedding syntax.
What are the chances tutorial 1001 changes that?&lt;/p></description></item><item><title>Pushing Past the First Error During Type Inference</title><link>https://thunderseethe.dev/posts/types-base/</link><pubDate>Mon, 10 Nov 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/types-base/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>Today&amp;rsquo;s post builds on the previous &lt;a 
 href="https://thunderseethe.dev/posts/unification/"
 
 >
 type inference post
 
&lt;/a>
 for the base language.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>&lt;a 
 href="https://thunderseethe.dev/posts/unification"
 
 >
 Last we left off
 
&lt;/a>
, we had a complete and working type inference system.
An admirable achievement and at the time our completed product.
I went off to write the remainder of the making a language series, and you went off to read it (I hope).
As I&amp;rsquo;ve written more of the language, my ambitions have focused.
Originally this series set out to write a compiler, pretty much any compiler would do and be challenging enough.
Now, however, it won&amp;rsquo;t be any old compiler, but a query-based compiler, supporting the &lt;a 
 href="https://microsoft.github.io/language-server-protocol/"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://microsoft.github.io/language-server-protocol/"
 data-goatcounter-title="Language Server Protocol (LSP)"
 data-goatcounter-referrer="Pushing Past the First Error During Type Inference"
 >
 Language Server Protocol (LSP)
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
.&lt;/p></description></item><item><title>Wasm Does not Stand for WebAssembly</title><link>https://thunderseethe.dev/posts/wasm-not-webassembly/</link><pubDate>Tue, 15 Jul 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/wasm-not-webassembly/</guid><description>&lt;p>&amp;ldquo;Wasm does not stand for WebAssembly&amp;rdquo; might sound like an outrageous claim.
A glance at &lt;a 
 href="https://webassembly.org"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://webassembly.org"
 data-goatcounter-title="webassembly.org"
 data-goatcounter-referrer="Wasm Does not Stand for WebAssembly"
 >
 webassembly.org
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
 confirms it&amp;rsquo;s not just how it sounds:&lt;/p>
&lt;p>&lt;img src="https://thunderseethe.dev/img/webassembly_org.png" alt="A photo of webassembly.org confirming Wasm does in fact stand for WebAssembly">&lt;/p>
&lt;p>Fortunately, I&amp;rsquo;ve never been one to let reason stand in the way of a good point.
Let&amp;rsquo;s talk about Wasm&amp;rsquo;s relationship to the name WebAssembly.&lt;/p>
&lt;p>As a proponent of Wasm (lengthened &lt;em>WebAssembly&lt;/em>), I spend a lot of time preaching its positives to the people.
Whilst standing on my soapbox, I encounter two common misunderstandings about Wasm:&lt;/p></description></item><item><title>Skipping the Backend by Emitting Wasm</title><link>https://thunderseethe.dev/posts/emit-base/</link><pubDate>Fri, 13 Jun 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/emit-base/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>Today&amp;rsquo;s post is preceded by the &lt;a 
 href="https://thunderseethe.dev/posts/closure-convert-base"
 
 >
 base closure conversion pass
 
&lt;/a>
.
Code emission turns our closure conversion IR into our executable target: WebAssembly.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>Today is a great day.
We stand together on the precipice of execution.
All the work we&amp;rsquo;ve done till now culminates in our final compilation pass: Code Generation.
Our compiler&amp;rsquo;s tires meet road, as we take our closure converted &lt;code>IR&lt;/code> and turn it into executable code, driving off into the sunset.&lt;/p></description></item><item><title>Closure Conversion Takes the Function out of Functional Programming</title><link>https://thunderseethe.dev/posts/closure-convert-base/</link><pubDate>Wed, 14 May 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/closure-convert-base/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>Today&amp;rsquo;s post is preceded by the &lt;a 
 href="https://thunderseethe.dev/posts/monomorph-base"
 
 >
 base monomorphization pass
 
&lt;/a>
.
Like monomorphization, closure conversion does not rely on anything from its prior pass.
It relies on the &lt;code>IR&lt;/code> (and accompanying types) introduced in &lt;a 
 href="https://thunderseethe.dev/posts/lowering-base-ir"
 
 >
 lowering
 
&lt;/a>
.
We&amp;rsquo;ll review &lt;code>IR&lt;/code> before the action starts.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>Our &lt;a 
 href="https://thunderseethe.dev/posts/monomorph-base"
 
 >
 previous pass
 
&lt;/a>
, monomorphization, stripped polymorphism away from our intermediate representation (IR).
Today we&amp;rsquo;ve come to cut down an even closer confidant, functions.
How many friends must we lose on our conquest of compilation.
This may come as a shock, functions are entwined deeply in our language.
I mean it&amp;rsquo;s right there in the name, functional programming.
I don&amp;rsquo;t know what al programming is, and I don&amp;rsquo;t want to.&lt;/p></description></item><item><title>Casting out Polymorphism with Monomorphization</title><link>https://thunderseethe.dev/posts/monomorph-base/</link><pubDate>Sun, 11 May 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/monomorph-base/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>Today&amp;rsquo;s post is preceded by the &lt;a 
 href="https://thunderseethe.dev/posts/simplify-base/"
 
 >
 base simplify pass
 
&lt;/a>
.
That pass, however, is not a prerequisite to understanding monomorphization.
Monomorphization relies only on the &lt;code>IR&lt;/code> introduced in &lt;a 
 href="https://thunderseethe.dev/posts/lowering-base-ir/"
 
 >
 lowering
 
&lt;/a>
.
We&amp;rsquo;ll review &lt;code>IR&lt;/code> before the action starts.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>As we press deeper into the compiler, we strip away high level features that stand between us and machine code.
Many of our language&amp;rsquo;s features have no direct instructions in the hardware, and we must either remove them or translate them into something that does.
The feature on today&amp;rsquo;s chopping block is polymorphism.&lt;/p></description></item><item><title>Back to Basics by Simplifying our IR</title><link>https://thunderseethe.dev/posts/simplify-base/</link><pubDate>Wed, 30 Apr 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/simplify-base/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language Series&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>Today&amp;rsquo;s post is preceded by the &lt;a 
 href="https://thunderseethe.dev/posts/lowering-base-ir/"
 
 >
 base lowering pass
 
&lt;/a>
.
From that post we&amp;rsquo;ll need our &lt;code>IR&lt;/code>, and it&amp;rsquo;s accompanying data types.
These will be covered in a refresher below.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>Today we&amp;rsquo;re talking about a magical aspect of compilation: optimization.
Magic both in its ability to peel away abstractions, leaving only efficient machine code, and its inscrutable behavior.
Optimizers are known for being black boxes with a million knobs, each serving as butterfly wings flapping towards the final result.&lt;/p></description></item><item><title>Function Application Needs to Grow a Spine Already</title><link>https://thunderseethe.dev/posts/function-application-grow-a-spine/</link><pubDate>Mon, 31 Mar 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/function-application-grow-a-spine/</guid><description>&lt;p>Function application can be found nestled into the heart of basically every functional language.
At the risk of hyperbole, I would even say every programming language.
Unlike languages inheriting from the C family, function application in functional languages makes use of &lt;a 
 href="https://en.wikipedia.org/wiki/Currying"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://en.wikipedia.org/wiki/Currying"
 data-goatcounter-title="currying"
 data-goatcounter-referrer="Function Application Needs to Grow a Spine Already"
 >
 currying
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
.
Currying lets us eschew multi argument functions in favor of a bunch of single argument functions that return more functions.
Function application (and currying) herald all the way back to the dawn of the lambda calculus:&lt;/p></description></item><item><title>Lowering Top Level Items</title><link>https://thunderseethe.dev/posts/lowering-items/</link><pubDate>Tue, 04 Mar 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/lowering-items/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This post lowers top level functions from our &lt;a 
 href="https://thunderseethe.dev/posts/check-top-level-items"
 
 >
 prior pass
 
&lt;/a>
.
It extends the row language with support for items (top level functions) in our IR.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>&lt;a 
 href="https://thunderseethe.dev/posts/lowering-rows-intro"
 
 >
 Last time
 
&lt;/a>
 saw us endeavoring upon a trilogy to bring rows crashing down from their high-flying AST nodes into the realities of our lowly IR.
Our goals this time are less highfalutin.
We&amp;rsquo;re going to be lowering items.
I can&amp;rsquo;t tell you how relieved I am to see this titled &amp;ldquo;.Items&amp;rdquo;, not &amp;ldquo;.Items[0]&amp;rdquo;.&lt;/p></description></item><item><title>The Heart of Lowered Rows</title><link>https://thunderseethe.dev/posts/lowering-rows-ast/</link><pubDate>Wed, 26 Feb 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/lowering-rows-ast/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This post continues lowering rows from our &lt;a 
 href="https://thunderseethe.dev/posts/lowering-rows-ty"
 
 >
 previous post
 
&lt;/a>
.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>&lt;a 
 href="https://thunderseethe.dev/posts/lowering-rows-ty"
 
 >
 Last time
 
&lt;/a>
 we upgraded &lt;code>lower_ty_scheme&lt;/code> to support rows and saw how we&amp;rsquo;d use it&amp;rsquo;s evidence to inform &lt;code>lower_ast&lt;/code>.
We&amp;rsquo;re getting to the heart of lowering rows this time: generating and applying our evidence terms.&lt;/p></description></item><item><title>The Types of Lowered Rows</title><link>https://thunderseethe.dev/posts/lowering-rows-ty/</link><pubDate>Tue, 18 Feb 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/lowering-rows-ty/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This post continues lowering rows from our &lt;a 
 href="https://thunderseethe.dev/posts/lowering-rows-intro"
 
 >
 previous post
 
&lt;/a>
.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>&lt;a 
 href="https://thunderseethe.dev/posts/lowering-rows-intro"
 
 >
 Last time
 
&lt;/a>
 we learned how we&amp;rsquo;re going to lower row types and made some row addendums for &lt;code>IR&lt;/code> and &lt;code>Type&lt;/code>.
Today, we&amp;rsquo;ll update &lt;code>lower_ty_scheme&lt;/code>, using those addendums, to generate types for &lt;code>Evidence&lt;/code>.&lt;/p>
&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Quick Refresher&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>Here&amp;rsquo;s a quick rundown of the stuff we&amp;rsquo;ll need from &lt;a 
 href="https://thunderseethe.dev/posts/row-types"
 
 >
 types/rows
 
&lt;/a>
.&lt;/p></description></item><item><title>Lowering Row Types, Evidently</title><link>https://thunderseethe.dev/posts/lowering-rows-intro/</link><pubDate>Tue, 11 Feb 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/lowering-rows-intro/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This post undertakes lowering rows building on our &lt;a 
 href="https://thunderseethe.dev/posts/row-types"
 
 >
 previous pass
 
&lt;/a>
 that inferred row types.
It extends the &lt;a 
 href="https://thunderseethe.dev/posts/lowering-base-ir"
 
 >
 lowering
 
&lt;/a>
 we did for the base language.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>&lt;a 
 href="https://thunderseethe.dev/posts/lowering-base-ir"
 
 >
 Last time
 
&lt;/a>
, we lowered &lt;a 
 href="https://github.com/thunderseethe/making-a-language/tree/main/types/base"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://github.com/thunderseethe/making-a-language/tree/main/types/base"
 data-goatcounter-title="types/base"
 data-goatcounter-referrer="Lowering Row Types, Evidently"
 >
 types/base
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
 into our IR.
That was exciting, but I professed some anxieties that all we had really done is swap an &lt;code>Ast::&lt;/code> prefix for an &lt;code>IR::&lt;/code> prefix.
Today, we&amp;rsquo;re going to assuage those concerns by lowering &lt;a 
 href="https://github.com/thunderseethe/making-a-language/tree/main/types/rows"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://github.com/thunderseethe/making-a-language/tree/main/types/rows"
 data-goatcounter-title="types/rows"
 data-goatcounter-referrer="Lowering Row Types, Evidently"
 >
 types/rows
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
.
A more seismic lowering.
Recall that &lt;a 
 href="https://thunderseethe.dev/posts/row-types"
 
 >
 row types
 
&lt;/a>
 provide data types for our language.
Rows are great in the surface language and the type checker but start to lose their luster once we need to represent them in memory.&lt;/p></description></item><item><title>Escaping the Typechecker, an Implementation</title><link>https://thunderseethe.dev/posts/lowering-base-impl/</link><pubDate>Tue, 04 Feb 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/lowering-base-impl/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This post builds on the &lt;a 
 href="https://thunderseethe.dev/posts/lowering-base-ir"
 
 >
 previous lowering post
 
&lt;/a>
.
Now that we have some more theory about lowering we undertake Implementation this time around.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>Armed with the knowledge of &lt;code>IR&lt;/code> and &lt;code>Type&lt;/code> from &lt;a 
 href="https://thunderseethe.dev/posts/lowering-base-ir"
 
 >
 last time
 
&lt;/a>
, we can finally write some code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#b0c4de;background-color:#282c34;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-rs" data-lang="rs">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#c678dd">fn&lt;/span> &lt;span style="color:#00b1f7">lower&lt;/span>(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c1abea">ast&lt;/span>: &lt;span style="color:#76a9f9">Ast&lt;/span>&lt;span style="color:#c7bf54">&amp;lt;&lt;/span>&lt;span style="color:#c1abea">TypedVar&lt;/span>&lt;span style="color:#c7bf54">&amp;gt;&lt;/span>, 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c1abea">scheme&lt;/span>: &lt;span style="color:#76a9f9">ast&lt;/span>::&lt;span style="color:#c1abea">TypeScheme&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>) -&amp;gt; (&lt;span style="color:#b756ff;font-weight:bold">IR&lt;/span>, &lt;span style="color:#c1abea">Type&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c678dd">let&lt;/span> (&lt;span style="color:#c1abea">ir_ty&lt;/span>, &lt;span style="color:#c1abea">types&lt;/span>) &lt;span style="color:#c7bf54">=&lt;/span> &lt;span style="color:#c1abea">lower_ty_scheme&lt;/span>(&lt;span style="color:#c1abea">scheme&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#00b1f7">todo!&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>One line down.
That&amp;rsquo;s great progress (especially compared to last post)!&lt;/p></description></item><item><title>Lowering our AST to Escape the Typechecker</title><link>https://thunderseethe.dev/posts/lowering-base-ir/</link><pubDate>Tue, 28 Jan 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/lowering-base-ir/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This post covers turning our typed AST into our intermediate representation for our base language.
It builds on the outputs of the &lt;a 
 href="https://thunderseethe.dev/posts/type-inference"
 
 >
 previous pass
 
&lt;/a>
 where we inferred types for the base language.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>We&amp;rsquo;ve been in &lt;a 
 href="https://thunderseethe.dev/posts/check-top-level-items"
 
 >
 type checking
 
&lt;/a>
 so long it&amp;rsquo;s becoming a tar pit deep enough to rival picking a parser.
Our only hope of escape is to delve deeper, lest we find ourselves fretting over the endlessly enticing type checker features available to adjoin.
We&amp;rsquo;re always free to return to our type checker older and wiser.
But this series is called making a language, not type check until our motivation evaporates.&lt;/p></description></item><item><title>DeBruijn Indices: Picking Equatable Names</title><link>https://thunderseethe.dev/posts/debruijn-indices/</link><pubDate>Thu, 23 Jan 2025 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/debruijn-indices/</guid><description>&lt;p>Naming things is hard.
It&amp;rsquo;s one of two hard problems in computer science, alongside cache invalidation and off by one errors.
Humans struggle to come up with good names.
Personally, I can never remember if it&amp;rsquo;s &lt;code>AbstractProducerFactory&lt;/code> or &lt;code>AbstractFactoryProducer&lt;/code>.&lt;/p>
&lt;p>Perhaps more surprisingly, computers are also stumped by this task.
Compilers often need to come up with names and find themselves at a loss just like you and me.
Sure, some of the names will be given to the compiler by the user writing code:&lt;/p></description></item><item><title>Traits are a Local Maxima</title><link>https://thunderseethe.dev/posts/traits-are-a-local-maxima/</link><pubDate>Mon, 18 Nov 2024 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/traits-are-a-local-maxima/</guid><description>&lt;p>Today we&amp;rsquo;re talking about &lt;a 
 href="https://doc.rust-lang.org/book/ch10-02-traits.html"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://doc.rust-lang.org/book/ch10-02-traits.html"
 data-goatcounter-title="Rust traits"
 data-goatcounter-referrer="Traits are a Local Maxima"
 >
 Rust traits
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
.
As you might&amp;rsquo;ve guessed from the title, we will not be talking about them positively.
Woah woah, hang on, put down your pitchfork.
A local maximum is still a maximum, I love traits as much as anyone.&lt;/p>
&lt;p>Traits are one of the few programming language concepts beloved enough to earn multiple names.
You might have heard about typeclasses in Haskell or protocols in Swift.
Heck, if the teacher&amp;rsquo;s not watching, even interfaces can be considered a kind of trait.
Elm famously left out traits, and it was so requested it spawned an &lt;a 
 href="https://faq.elm-community.org/#does-elm-have-ad-hoc-polymorphism-or-typeclasses"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://faq.elm-community.org/#does-elm-have-ad-hoc-polymorphism-or-typeclasses"
 data-goatcounter-title="FAQ"
 data-goatcounter-referrer="Traits are a Local Maxima"
 >
 FAQ
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
 explaining their absence.&lt;/p></description></item><item><title>Checking Types of Top Level Functions</title><link>https://thunderseethe.dev/posts/check-top-level-items/</link><pubDate>Fri, 05 Jul 2024 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/check-top-level-items/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This post covers type checking for top level functions.
It extends the row language with top level functions and type checking.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>&lt;a 
 href="https://thunderseethe.dev/posts/row-types"
 
 >
 Last season
 
&lt;/a>
, boy how time flies, we added support for algebraic datatypes using row types.
With &lt;strong>both&lt;/strong> product and sum types, our type inference must be near completion.
It&amp;rsquo;s a little lacking in scope though.
We don&amp;rsquo;t have a way to call top-level functions.&lt;/p></description></item><item><title>I'm Betting on Call-by-Push-Value</title><link>https://thunderseethe.dev/posts/bet-on-cbpv/</link><pubDate>Sat, 09 Mar 2024 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/bet-on-cbpv/</guid><description>&lt;p>You come upon a function argument at a fork in the road.
If it takes the left road, it&amp;rsquo;ll evaluate itself and then be passed to its function.
If it takes the right road, it&amp;rsquo;ll pass itself to the function to be evaluated somewhere down the road (🥁🐍).
Let&amp;rsquo;s bet on which road will be faster.&lt;/p>
&lt;p>We might suspect this is a rather boring bet.
All we have to do is look down each road and see which one is shorter.
Fortunately for our wager (and to the dismay of theorists everywhere), this is not the case.
We can always construct a situation where evaluating either eagerly or lazily is better.&lt;/p></description></item><item><title>In Search of the Perfect Fold</title><link>https://thunderseethe.dev/posts/in-search-of-the-perfect-fold/</link><pubDate>Mon, 27 Nov 2023 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/in-search-of-the-perfect-fold/</guid><description>&lt;p>I&amp;rsquo;m writing a compiler in Rust.
Which is to say, I do a lot of tree folds in Rust.
Rust shines at a lot of things.
Overall, I&amp;rsquo;m happy with my choice to use it.
It&amp;rsquo;s just&amp;hellip;for the life of me I can&amp;rsquo;t write a satisfactory fold over trees.&lt;/p>
&lt;p>Perhaps it&amp;rsquo;s a personal failing.
The perfect fold is out there, and I&amp;rsquo;m simply ignorant of its divine design.
If you&amp;rsquo;re reading this and thinking that, please I beg of you, enlighten me.
If you&amp;rsquo;re reading this and not thinking that, first off thank you, and second off welcome to my tar pit.&lt;/p></description></item><item><title>Rowing Afloat Datatype Boats</title><link>https://thunderseethe.dev/posts/row-types/</link><pubDate>Sat, 21 Oct 2023 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/row-types/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This post covers type inference for row types, a method for adding data types to our language.
It extends the base language with support for row types and their constructs.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>&lt;a 
 href="https://thunderseethe.dev/posts/unification"
 
 >
 Last episode
 
&lt;/a>
 we assembled a simple type inference engine end to end.
Powerful enough to check both functions &lt;em>and&lt;/em> integers, without any annotations at all.
Perfect as it is, our type inference (and language) lacks one teensy feature.
It doesn&amp;rsquo;t have any data types.
We&amp;rsquo;ll rectify that on today&amp;rsquo;s episode by adding support for row types.&lt;/p></description></item><item><title>What's in a Module?</title><link>https://thunderseethe.dev/posts/whats-in-a-module/</link><pubDate>Mon, 31 Jul 2023 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/whats-in-a-module/</guid><description>&lt;h2 id="that-which-we-call-a-module">
 That Which We Call a Module
 &lt;a class="heading-link" href="#that-which-we-call-a-module">
 &lt;i class="fa fa-link" aria-hidden="true" title="Link to heading">&lt;/i>
 &lt;span class="sr-only">Link to heading&lt;/span>
 &lt;/a>
&lt;/h2>
&lt;blockquote>
&lt;p>A module by any other namespace would encapsulate just as sweet&lt;/p>
&lt;ul>
&lt;li>Shakespeare&amp;hellip;mostly&lt;/li>
&lt;/ul>&lt;/blockquote>
&lt;p>&lt;a 
 href="https://en.wikipedia.org/wiki/Modular_programming"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://en.wikipedia.org/wiki/Modular_programming"
 data-goatcounter-title="Modular programming"
 data-goatcounter-referrer="What&amp;#39;s in a Module?"
 >
 Modular programming
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
 has become a cornerstone of modern software development.
Software projects are only getting larger, and as they do the need to divide up monolithic codebases becomes indispensable.
It is no surprise then, that some form of modules show up in all modern programming languages in use today:&lt;/p></description></item><item><title>Tying up Type Inference</title><link>https://thunderseethe.dev/posts/unification/</link><pubDate>Sat, 01 Jul 2023 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/unification/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This is the capstone of type inference we built towards with &lt;a 
 href="https://thunderseethe.dev/posts/bidirectional-constraint-generation"
 
 >
 constraint generation
 
&lt;/a>
.&lt;/p>
&lt;/div>
&lt;/details>

&lt;h2 id="constraint-solving">
 Constraint Solving
 &lt;a class="heading-link" href="#constraint-solving">
 &lt;i class="fa fa-link" aria-hidden="true" title="Link to heading">&lt;/i>
 &lt;span class="sr-only">Link to heading&lt;/span>
 &lt;/a>
&lt;/h2>
&lt;p>&lt;a 
 href="https://thunderseethe.dev/posts/bidirectional-constraint-generation"
 
 >
 On last week&amp;rsquo;s episode
 
&lt;/a>
 we generated a set of constraints with our bidirectional type system.
Now that we&amp;rsquo;ve got our set of constraints, we can attempt to solve them.
So what does it mean to solve a constraint? A constraint is something that has to be true about our types.
If we can prove whatever our constraint wants true, we have solved the constraint.
Since our &lt;code>Constraint&lt;/code> datatype is just type equality, we can solve all our constraints by proving their types equal.
If we fail to prove two types equal, we have a type error, and we can bail out early.&lt;/p></description></item><item><title>Turning our AST Into Type Constraints</title><link>https://thunderseethe.dev/posts/bidirectional-constraint-generation/</link><pubDate>Sat, 24 Jun 2023 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/bidirectional-constraint-generation/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>Today&amp;rsquo;s post builds on the previous &lt;a 
 href="https://thunderseethe.dev/posts/type-inference/"
 
 >
 type inference post
 
&lt;/a>
 for the base language.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>&lt;a 
 href="https://thunderseethe.dev/posts/type-inference/"
 
 >
 Last time
 
&lt;/a>
, we laid out the AST and Type for the language we are building. We also got a bird&amp;rsquo;s-eye view of our type inference algorithm: constraint generation, constraint solving, substitute our solved types. This time we&amp;rsquo;re implementing the constraint generation portion of our type inference algorithm.&lt;/p></description></item><item><title>Typechecking a Language Without a Parser</title><link>https://thunderseethe.dev/posts/type-inference/</link><pubDate>Sat, 17 Jun 2023 00:00:00 +0000</pubDate><guid>https://thunderseethe.dev/posts/type-inference/</guid><description>&lt;details class="accessory">
 &lt;summary>&lt;div class="title">Making a Language&lt;/div>&lt;/summary>
 &lt;div class="inner">&lt;p>This post is part of the &lt;a 
 href="https://thunderseethe.dev/series/making-a-language"
 
 >
 making a language series
 
&lt;/a>
.
A series that teaches you how to implement a programming language using Rust.&lt;/p>
&lt;p>This is the first post in the series, talking about type inference for a simple functional language.&lt;/p>
&lt;/div>
&lt;/details>

&lt;p>I&amp;rsquo;d like to design a language, more specifically implement a compiler for a programming language I&amp;rsquo;ve made up.
This is not the first time I&amp;rsquo;ve wanted to do this.
In fact, I&amp;rsquo;ve had the &lt;a 
 href="https://github.com/thunderseethe/waht"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://github.com/thunderseethe/waht"
 data-goatcounter-title="itch"
 data-goatcounter-referrer="Typechecking a Language Without a Parser"
 >
 itch
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
 &lt;a 
 href="https://github.com/thunderseethe/panera"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://github.com/thunderseethe/panera"
 data-goatcounter-title="quite a"
 data-goatcounter-referrer="Typechecking a Language Without a Parser"
 >
 quite a
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
 &lt;a 
 href="https://github.com/thunderseethe/brainfuck_interpreter"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://github.com/thunderseethe/brainfuck_interpreter"
 data-goatcounter-title="few"
 data-goatcounter-referrer="Typechecking a Language Without a Parser"
 >
 few
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
 &lt;a 
 href="https://github.com/thunderseethe/false_interpreter"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://github.com/thunderseethe/false_interpreter"
 data-goatcounter-title="times"
 data-goatcounter-referrer="Typechecking a Language Without a Parser"
 >
 times
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
 &lt;a 
 href="https://github.com/thunderseethe/tiger"
 
 
 target="_blank" 
 rel="noopener" 
 data-goatcounter-click="https://github.com/thunderseethe/tiger"
 data-goatcounter-title="before"
 data-goatcounter-referrer="Typechecking a Language Without a Parser"
 >
 before
 
 &lt;i class="fa-solid fa-arrow-up-right-from-square">&lt;/i>
 
&lt;/a>
.
I can&amp;rsquo;t tell you why I keep returning to this venture when I&amp;rsquo;ve failed at it so many times.
What I can tell you is why I always fail.
Every time I begin a sparkly new project with fresh eyes and quickly whip together a lexer.&lt;/p></description></item></channel></rss>