Published 2026-04-22
This is a demo of a toy language with dynamic typing, inline values, stack allocation, interior pointers, single ownership, and a limited form of borrowing - less expressive than rust, but much more expressive than second-class references (eg we can express external iterators).
Since there is no static typing the borrows must be checked dynamically. The interesting part of the demo is that we can do that fairly cheaply and with useful error messages.
The code is here.
background
I'm exploring a style of type-system exemplified by julia and zig. Both languages start with a dynamic type system, enforced by dynamic type-checks, and then layer on a static type system which is capable of proving that the dynamic type-checks are unnecessary. The dynamic type system provides flexibility and easy meta-programming, while the static type system removes the overhead in most of your code.
Julia and zig differ slightly in how they handle code that cannot be statically type-checked. Zig will refuse to compile the code at all, while julia will leave some dynamic type-checks and will run the static type-checks again when more type information is available.
For zest I'm exploring a third option - code can either be dynamically typed (and interpreted), or statically typed (and compiled), but switching between the two requires explicit annotations. The goal is that most of your code can have the assurances of static typing, but you can still opt in to dynamically-typed glue code to handle repls, live code reloading, runtime code generation, malleable software etc.
The tricky part is that I also want to enforce mutable value semantics. To date there are two main strategies for doing this:
Reference-counting and copy-on-write, which imposes an unpleasant performance overhead and is hard to combine with interior pointers / explicit stack allocation.
... continue reading