At a high level Haskell’s typeclasses and Rust’s traits are the same feature, but they also differ quite a bit in details. Here’s an example: struct A; trait C<T> {} impl<T> C<T> for Option<T> {} // ∀ T . C<Option<T>, T> fn f<P, Q>(p: P, q: Q) where P: C<Q> {} fn main() { f(None, A); } - {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-} data A = A class C a b instance C (Maybe t) t f :: C…
Fir macros are fully deterministic programs that are distributed separately and that can introspect into the using program’s type-checked AST definitions. Deterministic execution of macros is necessary to be able to Cache macro results in the language server and between compilation of a package during development. Avoid the issues with build scripts in many languages that can run arbitrary code…
A lesson we can derive from Rust’s error handling and async libraries, and Haskell’s effect system libraries, is that languages need to have opinionated (and efficient, flexible) interop features. If not and the language is flexible enough (with an expressive type system, and maybe also with metaprogramming features), users create their own solutions and the ecosystem gets fragmented. Consider…
I was just going through old files and saw a cool video of an old project that I thought I should share. In January 2021 I started working on a new text editor. Zed didn’t exist publicly at the time (it must’ve been under development) and two projects were getting a lot of hype: Alacritty because of its renderer Tree-sitter because IIRC Neovim was about to ship built-in support for tree-sitter…
One of my original goals with Fir was to bootstrap it as early as possible. I was so determined, I committed the first code for the self-hosted compiler in the 322nd commit , on 11 April 2025, after less than a year of development in the open source 1 , when it was barely usable. To understand how early this is, we’re currently on commit 1,052, and in my opinion it only recently became somewhat…
Robert Harper’s “Exceptions Are Shared Secrets” is an intriguing blog post, but it may come as a bit abstract unless you’re already familiar with the idea of accidental exception (or more generally, effect) handling, as the post has no code. In this post I want to give an example of the problems mentioned in the original post, and say a few words on how we might go about working around or fixing…
In the previous post we looked at a way to extend product types with new fields and sum types with new constructors, using row types, in Fir. A problem with the approach was that it required adding type parameters to the type being extended. In the cases where the extended type is a sum type and different constructors are extended with different fields, we may even need more than one type…
The front-end AST types are one of the most important types in a language implementation, and if we get them wrong nothing will be right in the rest of the implementation. These types should be cheap to allocate and efficient to use, but also extensible, as different tools will use them differently. A type checker may want to add inferred types to expressions, but for a formatter, those inferred…
Fir formats comments by assigning comment tokens to non-comment tokens (only conceptually, not in the implementation, see below), and generating comments when formatting the tokens that “own” them. This keeps AST nodes small. The parser doesn’t know about comments at all, and code that doesn’t care about comments don’t allocate more or run more code for comments. Formatting source code with…
A few months ago I implemented a PEG parser generator in Fir. It parses its own grammar and it’s also used to parse Fir . This week I finished another sizable 1 Fir project: a code formatter for Fir . It now formats most of the Fir code in the repo 2 . Fir is being designed and implemented from day one with tooling, libraries, and backwards compatibility in mind. The compiler’s front-end is…