RSS Amplifier

Bit Maybe Wise · Apr 21, 2026

Tsonnet #38 - Call me maybe, but make it typed, part 4

0
Sign in to vote or save

Hercules Merscher · Bit Maybe Wise

Welcome to the Tsonnet series!

If you’re not following along, check out how it all started in the first post of the series.

In the previous post, we implemented closures — both bound to locals and immediately invoked:

I was annoyed by the fact that function type variants were difficult to read, so went on an errand to make it simpler. We can do better!

Let’s see how it turned out.

ClosureCall is gone. It’s basically FunctionCall, so there’s no need to have a duplicated type variant.

The type variants function_def, function_call, and closure are represented as records, which makes them much easier to reason about since we can have names to represent what they are holding:

With this AST change, it became easier to simplify the parsing rules:

I could finally move closure out of prog and expr. It was never meant to be there. But part of the problem was not having FUNCTION flagged as non-associative.

With ClosureCall gone and FunctionCall now carrying a callee: expr, the interpreter needed a bit more surgery -- resolving the callee before dispatching to apply_function:

The type checker can also be improved by using the new type variants as records:

Switching from tuples to named records across the AST, type checker, and interpreter was one of those refactors that feels almost mechanical — until it isn’t. The real win was collapsing ClosureCall into FunctionCall and generalising the callee to an arbitrary expression. The grammar conflict that made closure a second-class citizen in the previous post? Gone. A bit of precedence annotation and a small restructuring of funcall was all it took.

Here is the entire diff.

Next up, methods!

Read the original on bitmaybewise.substack.com

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.