The Surface of Intent
It's been two months since my last entry. In April I wrote about a principle I was trying to live by: compute only what matters. I figured that between the startup, the family, and a few creative projects, the journal would stay quiet for a while longer.
But projects have a way of insisting on their own completion. For the past few months, and honestly the past few years, most of my spare thinking has gone to one thing, close to the metal. Today I finally pushed it live.
Twenty years ago, as a kid, I dreamed of building my own programming language. I watched Ruby take off out of Japan and wanted to make something meaningful from Korea. I spent two decades tinkering in the dark. Topaz v1 started, strangely enough, as a Lisp dialect, which eventually spun off into a tiny separate language at lispex.com. v2 moved toward a more Python-like expressiveness and ease. Around that time I fell in love with Rust and decided that "powerful" had to mean Rust-level safety and performance.
Then the AI era arrived, and I had a small existential crisis. If machines are writing the code, why would anyone bother learning a new language? What was the point of Topaz?
The answer sent me back to re-engineer the whole thing around subtraction. When software is written by people and agents together, a big, shifting language surface is a liability. It's where ambiguity creeps in, and where a model starts hallucinating logic. So v5.2 went the other way: a small surface, fixed and predictable, with one obvious way to say each thing. It reads roughly like Python or TypeScript, runs on a reference interpreter (topaz run), and builds a self-contained native binary by lowering the code to Rust and handing it to the Rust toolchain (topaz build).
Here is the whole feel of it in a few lines:
function 인사하기(이름: string) -> string {
return "안녕하세요, {이름}님!"
}
print(인사하기("토파즈"))
// 안녕하세요, 토파즈님!
The function's name is in Korean because it can be. The lexer treats it as an identifier like any other, with no romanizing and no quiet normalization underneath; a name compares equal only when its scalar sequence does. Twenty years ago that was the part I wanted and couldn't build, a language where a Korean word stays a Korean word.
It didn't come together in the abstract. Through the v3 and v4 cycles, Topaz quietly became the workhorse for our team at STUDIO HAZE. We used it for the core application logic in our upcoming services, a deterministic code translation platform and Atlas, the Korean typesetting app I mentioned here back in April. It survived the ordinary friction of production long before I thought about showing it to anyone.
Under that surface, the rules are deliberately rigid, and those are the parts I'm proudest of. The whole toolchain (lexer, parser, checker, interpreter, emitter) is plain Rust with no external crates and forbid(unsafe_code) set across the workspace. To make sure the interpreter and the compiled binary never quietly disagree, every test fixture runs through both, and the two have to return the same output and the same exit status. The moment they diverge, the build goes red. Right now that suite is 635 functions, all passing.
The boundaries are built the same way. sql, sh, and path strings aren't ordinary strings; they lower to typed templates that keep the values you interpolate separate from the literal text. Nothing you pass in gets spliced into a query as raw SQL, so a whole class of injection bug stops being something you have to remember to prevent and becomes something the grammar won't let you write. Even the CI carries the same temperament: it runs on Linux, macOS, and Windows, because the differences between their filesystems push on the module resolver in ways Linux alone never would.
I leaned on AI heavily through the private years, both for writing the implementation and for reviewing it. I won't pretend otherwise, and in a way that was the real test of the design: a small, fixed surface is exactly the kind of thing an agent can write without wandering off. I'm not going to publish the prompts or the orchestration logs, though. What's worth inspecting is the public part, the source and the locked spec and the fixtures that hold the two engines to each other.
The repository is public as of today, at github.com/studiohaze/topaz. It's early, and there's no ecosystem to speak of yet. And it isn't "easy Rust." It's a small application surface that happens to compile through Rust.
Building a language taught me that power isn't trying to do everything. It's deciding what the surface will never do, and then holding that line. After twenty years of tinkering in the dark, getting that one idea right was worth all of it.