Jam – Programming Language (C-like, safe, no lifetime syntax)

redbar0n1 pts1 comments

Jam Programming Language — Raphael Amorim

-->

-->

Jam Programming Language

13 May • 2026

Before I get into any of this: I’m not bashing any language. I have real respect for all of them. Every language has good things the others don’t, and there’s no such thing as a perfect language. As the creator of Rio Terminal, I’ve never said a negative word about another terminal, and I won’t say one here about another language either. Anyone spending their time trying to build something they believe in has my admiration. What follows is a take on tradeoffs, not on the people behind the work.

One more thing: Jam is still on its way to v1.0. The mechanics below are real and running in the compiler today, but specifics may change before the language stabilizes, so treat what’s here as the current state and not the final form.

One more thing though: if you think learning a programming language in the age of LLMs is a waste of time, then this post isn’t for you. Saying it up front so you can save some of yours.

Ok, all being said. Lemme start.

I love Rust. I really do. I’ve run Rust workshops. I’ve been the person who pioneered Rust adoption at a couple of the companies I’ve worked at. I have spent a lot of energy trying to bring teams along.

The problem is that Rust keeps getting more complicated. Don’t take that the wrong way. Rust has a clear philosophy and is making the choices that philosophy demands. But in real-life work, on real teams, you want to ship things fast and you want the team committed to the language. The cliff between “I can write some Rust” and “I am productive in Rust” is steep enough that good engineers stall on it, and you spend months pulling them up.

This is why Zig has been striving lately. Zig keeps programmers close to C-like languages: small surface, immediate mental model, no syntactic noise. The joy of C without most of the C foot-cannons. The catch is that Zig isn’t a safe language. Uninitialized reads, manual cleanup, nothing at the language level stopping a use-after-free. Zig leaves all of that in your hands and trusts you to be careful.

You can argue that’s fine. A single experienced programmer working solo can hold the invariants in their head and is unlikely to ship a use-after-free. The problem is that real software is almost never that. Real software has dependencies, and a single CVE in any of them lands on you. Real teams mix experience levels, and the less senior end of the team makes mistakes more often. Look at big Zig or C++ projects in production: they lean heavily on Valgrind, AddressSanitizer, and fuzzing, running the same checks over the same flow over and over, sometimes inside dependencies the team doesn’t own. That’s the cost of unsafe-by-default. The verification work doesn’t disappear; it gets pushed out of the language and into tools, CI, and postmortems. Software ends up less reliable in production and harder to maintain over time.

Jarred Sumner, creator of Bun, on rewriting Bun from Zig to Rust. Exactly the tax described above.

In the age of AI, safety has become a must, or at least highly desirable. A lot of code in production today is written, or at least drafted, by something that isn’t a human. I am not making a value judgement; it is just where we are. The shape of the bottleneck has shifted: ten years ago you wrote code, now you review code. Ten years ago the compiler caught half the bugs and the human caught the other half; now the compiler has to catch all of them, because the human going line by line with full intent is gone or distracted. With code volume rising and review surface flat, the language has to be the one keeping things honest.

That’s why I started Jam programming language.

The question I’ve been working on: how do you keep the joyful, immediate feel of a C-like language (Go, Zig, modern C) while making the language safe without a garbage collector? How do you give people the C ergonomic without the C bug class? The compromise that fell out is a language that draws from four places. Today I’m focusing on two; the other two will get their own posts.

Mutable value semantics as described in Racordon, Abrahams et al. 2022.1 Bindings own their values, parameter borrows live only for the duration of a single function call, and no reference or lifetime syntax appears anywhere in user code. This is what replaces the borrow checker.

Rust’s drop system. Types declare a drop function, the compiler synthesizes the call at every scope exit, and a small dataflow analysis catches use-of-uninitialized at compile time.

The result is a language where: Bindings own their values and resources clean themselves up, so every binding of a drop-bearing type fires its drop function automatically when the binding goes out of scope.

Jam drop system

In Jam you write:

const File = struct {<br>fd: i32,<br>fn drop(self: mut File) {<br>close(self.fd);<br>};

export fn useFile() i32 {<br>const f: File = { fd: 7 };<br>return f.fd;

There is no explicit cleanup. No defer,...

language rust real drop programming code

Related Articles