52 comments

[ 0.25 ms ] story [ 76.0 ms ] thread
So, then if I want to use a certain terminal text editor to create a clone of it in nanolang, I'd end up typing nano nano.nano on the command line.

I might accidentally summon a certain person from Ork.

One novel part here is every function is required to have tests that run at compile time.

I'm still skeptical of the value add having to teaching a custom language to an LLM instead of using something like lua or python and applying constraints like test requirements onto that.

It seems that something that does away with human friendly syntax and leans more towards a pure AST representation would be even better? Basically a Lisp but with very strict typing might do the trick. And most LLMs are probably trained on lots of Lisps already.
Developed by Jordan Hubbard of NVIDIA (and FreeBSD).

My understanding/experience is that LLM performance in a language scales with how well the language is represented in the training data.

From that assumption, we might expect LLMs to actually do better with an existing language for which more training code is available, even if that language is more complex and seems like it should be “harder” to understand.

I feel like the time for this was two years ago, and LLMs are now less bothered by remembering syntax than I am. It's a nice lisp-y syntax though.
I went looking for a single Markdown file I could dump into an LLM to "teach" it the language and found this one:

https://github.com/jordanhubbard/nanolang/blob/main/MEMORY.m...

Optimistically I dumped the whole thing into Claude Opus 4.5 as a system prompt to see if it could generate a one-shot program from it:

  llm -m claude-opus-4.5 \
    -s https://raw.githubusercontent.com/jordanhubbard/nanolang/refs/heads/main/MEMORY.md \
    'Build me a mandelbrot fractal CLI tool in this language' 
   > /tmp/fractal.nano
Here's the transcript for that. The code didn't work: https://gist.github.com/simonw/7847f022566d11629ec2139f1d109...

So I fired up Claude Code inside a checkout of the nanolang and told it how to run the compiler and let it fix the problems... which DID work. Here's that transcript:

https://gisthost.github.io/?9696da6882cb6596be6a9d5196e8a7a5...

And the finished code, with its output in a comment: https://gist.github.com/simonw/e7f3577adcfd392ab7fa23b1295d0...

So yeah, a good LLM can definitely figure out how to use this thing given access to the existing documentation and the ability to run that compiler.

Really clean language where the design decisions have led to fewer traps (cond is a good choice).

It’s peculiar to see s-expressions mixed together with imperative style. I’ve been experimenting along similar lines - mixing s-expressions with ML style in the same dialect (for a project).

Having an agentic partner toiling away with the lexer/parser/implementation details is truly liberating. It frees the human to explore crazy ideas that would not have been feasible for a side/toy/hobby project earlier.

Looks a bit like Rust. My peeve with Rust is that it makes error handling too much donkey work. In a large class of programs you just care that something failed and you want a good description of that thing:

  context("Loading configuration from {file}")
Then you get a useful error message by unfolding all the errors at some point in the program that is makes sense to talk to a human, e.g. logs, rpc error etc.

Failed: Loading configuration from .config because: couldn't open file .config because: file .config does not exist.

It shouldn't be harder than a context command in functions. But somehow Rust conspires to require all this error type conversion and question marks. It it is all just a big uncomfortable donkey game, especially when you have nested closures forced to return errors of a specific type.

Just scanning through this, looks interesting and is totally needed, but I think it is missing showing future use-cases and discussions of decoding. So, for instance, it is all well and good to define a simple language focused on testing and the like, but what about live LLM control and interaction via a programming language? Sort of a conversation in code? Data streams in and function calls stream out with syntax designed to minimize mistakes in calls and optimize the stream? What I mean by this is special block declarations like:

``` #this is where functions are defined and should compile and give syntax errors ```

:->r = some(param)/connected(param, param, @r)/calls(param)<-:

(yeah, ugly but the idea is there) The point being that the behavior could change. In the streaming world it may, for instance, have guarantees of what executes and what doesn't in case of errors. Maybe transactional guarantees in the stream blocks compared to pure compile optimization in the other blocks? The point here isn't that this is the golden idea, but that we probably should think about the use cases more. High on my list of use cases to consider (I think)

- language independence: LLMs are multilingual and this should be multilingual from the start.

- support streaming vs definition of code.

- Streaming should consider parallelism/async in the calls.

- the language should consider cached token states to call back to. (define the 'now' for optimal result management, basically, the language can tap into LLM properties that matter)

Hmm... That is the top of my head thoughts at least.

I think the real gap in computer languages wrt LLMs is a replacement for python as a "notebook" language that the LLM uses to solve ad hoc problems during a chat.

What you want is something that is safe, performant, uses minimal tokens and takes careful note of effects and capabilities. Tests aren't really even important for that use case.

There’s both efficacy and token efficiency to consider here.

Seems unlikely for an out-of-distribution language to be as effective as one that’s got all the training data in the world.

Really needs an agent-oriented “getting started” guide to put in the context, and evals vs. the same task done with Python, Rust etc.

At this point, I am starting to feel like we don’t need new languages, but new ways to create specifications.

I have a hypothesis that an LLM can act as a pseudocode to code translator, where the pseudocode can tolerate a mixture of code-like and natural language specification. The benefit being that it formalizes the human as the specifier (which must be done anyway) and the llm as the code writer. This also might enable lower resource “non-frontier” models to be more useful. Additionally, it allows tolerance to syntax mistakes or in the worst case, natural language if needed.

In other words, I think llms don’t need new languages, we do.

coding in latex and then translating to the target via llm works remarkably well nowadays
Go read ai2027 and then be ashamed of yourself /s

But seriously, llms can transmit ideas to each other through English that we do understand, we are screwed if it’s another language lol

we have some markup for architectures like - d2lang, sequencediagram.org's, bpmn.io xmls (which are OMG XMLs), so question is - can we master these, and not invent new stuf for a while?

p.s. a combination of the above fares very well during my agentic coding adventures.

We're already at a point where I think a PR should start with an LLM prompt that fully specs out the change/feature.

And then we can look at multiple LLM-generated implementations to inform how the prompt might need to be updated further until it's a one-shot.

Now you have perfect intention behind code, and you can refine the intention if it's wrong.

> The benefit being that it formalizes the human as the specifier (which must be done anyway) and the llm as the code writer.

The code was always a secondary effect of making software. The pain is in fully specifying behavior.

I disagree I think we always need new languages. Every language over time becomes more and more unnecessarily complex.

It's just part of the software lifecycle. People think their job is to "write code" and that means everything becomes more and more features, more abstractions, more complex, more "five different ways to do one thing".

Many many examples, C++, Java esp circa 2000-2010 and on and on and on. There's no hope for older languages. We need simpler languages.

I think that we haven't even started to properly think about a higher-level spec language. The highest level objects would have to be the users and the value that they get out of running the software. Even specific features would have to be subservient to that, and would shift as the users requirements shift. Requirements written in a truly higher-level spec language would allow the software to change features without the spec itself changing.

This is where LLMs slip up. I need a higher-level spec language where I don't have to specify to an LLM that I want the jpeg crop to be lossless if possible. It's doubly obvious that I wouldn't want it to be lossy, especially because making it lossy likely makes the resulting files larger. This is not obvious to an LLM, but it's absolutely obvious if our objects are users and user value.

A truly higher-level spec language compiler would recognize when actual functionality disappeared when a feature was removed, and would weigh the value of that functionality within the value framework of the hypothetical user. It would be able to recognize the value of redundant functionality by putting a value on user accessibility - how many ways can the user reach that functionality? How does it advertise itself?

We still haven't even thought about it properly. It's that "software engineering" thing that we were in a continual argument about whether it existed or not.

Language is not the problem but clear intent along with direction of action and defined and not implied subject.

Consider:

"Eat grandma if you're hungry"

"Eat grandma, if you're hungry"

"Eat grandma. if you're hungry"

Same words and entirely different outcome.

Pseudo code to clarify:

[Action | Directive - Eat] [Subject - Grandma] [Conditional of Subject - if hungry]

Opus 4.5 is very good at using TLA+ specifications to generate code.
Seems like a simplified Rust with partial prefix notation (which the rationale that is better for LLMs is based on vibes really) that compiles to C. Similar language posted here not too long ago: Zen-C => more features, no prefix notation / Rue => no prefix notation, compiles directly to native code (no C target). Surprisingly compared to other LLM "optimized" languages, it isn't so much concerned about token efficiency.
Isn't this essentially just WebAssembly or Lisp?
Why did you pick shadow as a keyword for a testing block?
This is kinda frustrating, docs are LLM generated, no explanation or justification for how this was designed to be targeted for LLMs.
I think this kind of misses what's actually challenging with LLM code -- auditing it for correctness. LLMs are ~fine at spitting out valid syntax. Humans need to be able to read the output, though.
Why NanoLang? NanoLang solves three problems:

LLM Code Generation - Unambiguous syntax reduces AI errors

Testing Discipline - Mandatory tests improve code quality

Simple & Fast - Minimal syntax, native performance

Design Philosophy:

Minimal syntax (18 keywords vs 32 in C)

One obvious way to do things

Tests are part of the language, not an afterthought

Transpile to C for maximum compatibility

ehh. i dont think the overhead of inventing a new language makes up for the lack of data around it. in fact if you're close enough to rust/c then llms are MORE likely to make up stuff from their training data and screw up your minimal language.

(pls argue against this, i want to be proven wrong)

Almost no positive comments. I for one find this a great try, a very interesting project. I hope this kind of experiments gain track.
I feel this could be achieved better with Golang or Kotlin and a custom linter that enforces parentheses around each expression term to make precedence explicit, and enforce each function has at least one test. Although I guess neither of those languages has free interop with C, they are close. And Go doesn’t have unions :’(
A language targeting an LLM might be well served with a lot of keywords, similar to a CISC instruction set, where keywords do specific things well. Giving it building blocks and having them piece together is likely to pay off.
Author has a Wikipedia page about him linked from his GitHub profile: https://en.wikipedia.org/wiki/Jordan_Hubbard

Summary:

- Co-created FreeBSD.

- Led UNIX technologies at Apple for 13 years

- iXSystems, lead FreeNAS

- idk something about Uber

- Senior Director for GPU Compute Software at NVIDIA

For whatever it’s worth.

> tiny … language designed to be targeted by coding LLMs

so like Go?

> Key Features; Prefix Notation

wow

NEXT!

opus is currently the only one that can code rust, but if you give it symbol resolution there is quite literally nothing better. The type system in rust is incredibly powerful and llms are great (just opus for now) at utilizing it.
Thanks for all of the comments!

Quick reaction:

1. Nanolang is a total thought experiment. The key word its description is "experimental" - whether it's a Good experiment or a Bad experiment can be argued either way, especially by language purists!

2. Yes, it's a total Decorator Crab of a language. An unholy creation by Dr Frankenstein, yes! Those criticisms are entirely merited. It wasn't designed, it accreted features and was a fever dream I couldn't seem to stop having. I should probably take my own temperature.

3. I like prefix notation because my first calculator was an HP calculator (the HP 41C remains, to this day, my favorite calculator of ALL TIME). I won't apologize for that, but I DO get that it's not everybody's cup of tea! I do, however, use both vi and emacs now.

Umm. I think that about covers it. All of this LLM stuff is still incredibly young to me and I'm just firing a shotgun into the dark and listening to hear if I hit anything. It's going to be that way for a while for all of us until we figure out what works and what does not!

- jkh

There's nothing more fun than making a DSL, the only annoying part if finding an excuse to make one