11 ms·
Nanolang: A tiny experimental language designed to be targeted by coding LLMs
- jason_s 8mo agoInteresting. The syntax looks like C and Scheme had an illegitimate child together. (Don't get me wrong, I do like the unambiguity of prefix notation.)
- aixpert 8mo agothere was nothing nano about that language at all if you want true nano drop return keywoards or just go APL
- JamesTRexx 8mo agoSo, 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.
- jll29 8mo agoMake sure to create a "Getting Started" video with Nano Banana.
- spicybright 8mo agoOne 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.
- sinuhe69 8mo agoPyret, a teaching language for CS, in the vein of Racket, does require testing by writing functions. https://pyret.org/docs/latest/testing.html https://pyret.org/docs/latest/testing.html
- pmontra 8mo agoI'm not sure that it's novel but I'm skeptical about the noise to signal ratio for anything that is not an example. I think that a real world file of source code will be either completely polluted by tests (they are way longer than the actual code they test) or become fn process_order { ... } shadow process_order { assert test_process_order } and the test code will be written in another file, and every function in the test code will have its own shadow function asserting true, to please the compiler.
- prngl 8mo agoLooks nice!
- abraxas 8mo agoIt 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.
- verdverm 8mo agoGenerally seems a bad idea to have your LLM write languages you do not understand or write yourself
- catlifeonmars 8mo agoDoesn’t that apply to the OP as well?
- verdverm 8mo agoYes, I'm not going to fill my precious context with documentation for a programming language This seems like a research dead end to me, the fundamentals are not there
- catlifeonmars 8mo agoIt seems kind of silly that you can’t teach an LLM new tricks though, doesn’t it? This doesn’t sound like an intrinsic limitation and more an artifact of how we produce model weights today.
- verdverm 8mo agogetting tricks embedded into the weights is expensive, it doesn't happen in a single pass they's why we teach them new tricks on the fly (in-context learning) with instruction files
- catlifeonmars 8mo ago
- thorum 8mo agoDeveloped 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.
- whimsicalism 8mo agoeasy enough to solve with RL probably
- measurablefunc 8mo agoThere is no RL for programming languages. Especially ones w/ no significant amount of code.
- whimsicalism 8mo agonot even wrong
- measurablefunc 8mo agoExactly.
- nl 8mo agoI guess the op was implying that is something fixable fairly easily? (Which is true - it's easy to prompt your LLM with the language grammar, have it generate code and then RL on that) Easy in the sense of "it is only having enough GPUs to RL a coding capable LLM" anyway.
- measurablefunc 8mo agoIf you can generate code from the grammar then what exactly are you RLing? The point was to generate code in the first place so what does backpropagation get you here?
- boutell 8mo agoI 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.
- simonw 8mo agoI 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.md 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/7847f022566d11629ec2139f1d109fb8 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/index.html https://gisthost.github.io/?9696da6882cb6596be6a9d5196e8a7a5... And the finished code, with its output in a comment: https://gist.github.com/simonw/e7f3577adcfd392ab7fa23b1295d00f2 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.
- nodja 8mo agoI think you need to either feed it all of ./docs or give your agent access to those files so it can read them as reference. The MEMORY.md file you posted mentions ./docs/CANONICAL_STYLE.md and ./docs/LLM_CORE_SUBSET.md and they in turn mention indirectly other features and files inside the docs folder.
- simonw 8mo agoYeah, I think you're right about that. The thing that really unlocked it was Claude being able to run a file listing against nanolang/examples and then start picking through the examples that were most relevant to figuring out the syntax: https://gisthost.github.io/?9696da6882cb6596be6a9d5196e8a7a5/page-001.html#msg-2026-01-19T23-43-10-761Z https://gisthost.github.io/?9696da6882cb6596be6a9d5196e8a7a5...
- hahahahhaah 8mo ago
- sheepscreek 8mo agoReally 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.
- catlifeonmars 8mo agoI had the same reaction to seeing S-expressions, but my conclusion was that this should just be a lisp. Why stop just at operators?
- jkh99 8mo agoI posted about this also on linkedIn. I actually tried to do a bunch of agentic coding in Clojure but found that the parser really got in my way - it continuously fooled the LLM on what line number the errors actually occurred on! This is why I made nanolang always report line numbers accurately and also have a built-in "trace mode" where, with an environment variable set, it would tell you exactly what line number was producing which C code and which behaviors were being exhibited (state changes). The LLM uses this aggressively for debugging!
- catlifeonmars 8mo ago> it would tell you exactly what line number was producing which C code and which behaviors were being exhibited (state changes) Would you consider generating sourcemaps? Although they’re mainly used for minified JavaScript they sound like a perfect fit for this use case. Plus they’re so ubiquitous there’s already a ton of tooling that understands them.
- fizlebit 8mo agoLooks 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.
- wazzaps 8mo agoYou just described how the popular "anyhow" and "snafu" crates implement error handling
- fizlebit 8mo agoEven with anyhow there is a lot of boilerplate it seems to me dealing with crates that don’t use it. I haven’t tried snafu but its name does not inspire confidence. Clanker (ai assistant) also love to unwrap and if you don’t catch them you have an abort waiting for you.
- jll29 8mo agoI like your "context" proposal, because it adds information about developer intention to error diagnostics, whereas showing e.g. a call stack would just provide information about the "what?", not the "why?" to the end user facing an error at runtime. (You should try to get something like that into various language specs; I'd love you to success with it.) EDIT: typo fixed.
- jmward01 8mo agoJust 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.
- empath75 8mo agoI 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.
- derrida 8mo ago> 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. hey I found this project december 23 and you just commented on another thing I posted "amazing one shot that" I will give you an invite if you want (because it also does that) check bio will add contact dets now... it was posted to this site earlier about 20 days ago and front paged and hilariously about half the comments were shooting it down the top voted comment was even "this is the worst website ever" lol xD and they since invite only to manage abuse (its a very capable service and currently free) It's capable of what you just mentioned, and it made the other site that one-shot you said was amazing for the one shot (literally cut and paste the comment into the prompt, then 2nd was "Good, now do it better")
- cadamsdotcom 8mo agoThere’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.
- topspin 8mo ago> Really needs an agent-oriented “getting started” guide to put in the context, and evals vs. the same task done with Python, Rust etc. It has several such documents, including a ~1400 line MEMORY.md file referencing several other such files, a language specification, a collection of ~100 documents containing just about every thought Jordan has ever had about the entire language and the evolution of its implementation, and a collection of examples that includes an SDL2 based OpenGL program. Obviously, jkh clearly understands the need to bootstrap LLMs on his ~5 month old, self-hosted solo programming language.
- deepsquirrelnet 8mo agoAt 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.
- catlifeonmars 8mo agoAnd so it comes full circle XD.
- bigfishrunning 8mo agoSo in this case an LLM would just be a less-reliable compiler? What's the point? If you have to formally specify your program, we already have tools for that, no boiling-the-oceans required
- kamaal 8mo ago>>new ways to create specifications. Thats again programming languages. Real issue with LLMs now is it doesn't matter if it can generate code quickly. Some one still has to read, verify and test it. Perhaps we need a need a terse programming language. Which can be read quickly and verified. You could call that specification.
- deepsquirrelnet 8mo agoYes, essentially a higher level programming language than what we currently have. A programming language that doesn't have strict syntax, and can be expressed with words or code. And like any other programming language, it includes specifications for the tests and expectations of the result. The programming language can look more like code in parts where the specification needs to be very detailed. I think people can get intuition about where the LLM is unlikely to be successful. It can have low detail for boilerplate or code that is simple to describe. You should be able to alter and recompile the specification, unlike the wandering prompt which makes changes faster than normal version control practices keep up with. Perhaps there's a world where reading the specification rather than the compiled code is sufficient in order to keep cognitive load at reasonable levels. At very least, you can read compiled code until you can establish your own validation set and create statistical expectations about your domain. Principally, these models will always be statistical in nature. So we probably need to start operating more inside that kind of framework if we really want to be professional about it.
- forgotpwd16 8mo agoSeems 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.
- noduerme 8mo agoI find Polish or Reverse Polish notation jarring after a lifetime of thinking in terms of operator precedence. Given that it's fairly rare to see, I wonder what about it would be more LLM-friendly. It does lend itself better to "tokenization" of a sort - if you want to construct operations from lots of smaller operations, for example if you're mutating genetic algorithms (a la Eureqa). But I've written code in the past to explicitly convert those kinds of operations back to infix for easier readability. I wonder if the LLMs in this case are expected to behave a bit like genetic algorithms as they construct things.
- forgotpwd16 8mo ago>It does lend itself better to "tokenization" of a sort - if you want to construct operations from lots of smaller operations [...] That's an educated assumption to make. But therein lies the issue with every LLM "optimized" language, including those recent ones posted here oriented toward minimizing tokens. Assumptions, that are unvalidatable and unfalsifiable, about the kind of output LLMs synthesize/emit when that output is code (or any output to be real).
- stevefan1999 8mo agoIsn't this essentially just WebAssembly or Lisp?
- nurettin 8mo agoIt looks like a Frankenstein's abomination that has c-like function signatures and structs with Sexpr function bodies and this will anger some homomorphism nerds. I love it.
- stevefan1999 8mo agoI mean WAT (WebAssembly Text Format) is essentially like that
- nurettin 8mo agoYou mean WAT has C-like struct and function signature syntax? I'm not seeing it.
- stevefan1999 8mo agoOh, those are also inside the expression
- teaearlgraycold 8mo agoWhy did you pick shadow as a keyword for a testing block?
- firemelt 8mo agobecause every function followed by a shadow
- teaearlgraycold 8mo agoIf true this is a meme language
- refulgentis 8mo agoThis is kinda frustrating, docs are LLM generated, no explanation or justification for how this was designed to be targeted for LLMs.
- loeg 8mo agoI 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.
- Trufa 8mo agoAlmost no positive comments. I for one find this a great try, a very interesting project. I hope this kind of experiments gain track.
- swyx 8mo agoWhy 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)
- jitl 8mo agoI 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 :’(
- hsaliak 8mo agoA 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.
- jitl 8mo agoAuthor has a Wikipedia page about him linked from his GitHub profile: https://en.wikipedia.org/wiki/Jordan_Hubbard 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.
- topspin 8mo agoa.k.a. jkh. That's a blast from the past. Back in the early FreeBSD days, Jordan was fielding mailing list traffic and holding the project together as people peppered the lists with questions, trying to get their systems running with their sundry bits of hardware. I wondered when he slept. Apparently he did as well[1]: "The start of the 2.0 ports collection. No sup repository yet, but I'll make one when I wake up again.. :)" Submitted by: jkh Aug 21, 1994 [1] https://github.com/freebsd/freebsd-ports/commit/7ca702f09f2945c1baaadef9b7bd13bd867de422 https://github.com/freebsd/freebsd-ports/commit/7ca702f09f29... Interesting commit starting Ports 2.0. Three version of bash, four versions of Emacs, plus jove.
- pancsta 8mo ago> tiny … language designed to be targeted by coding LLMs so like Go? > Key Features; Prefix Notation wow NEXT!
- zozbot234 8mo agoEvery new language pet project these days claims to be "designed for LLM's", lol. Don't read too much into it. The only language that's really designed for LLM is COBOL, because it was written to read just like English natural language and LLM's are trained by reading lots of English language books.
- kachapopopow 8mo agoopus 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.
- stevedonovan 8mo agoCodex generates solid Rust in my experience. Just needs a little style guidance
- jkh99 8mo agoThanks 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
- willquack 8mo agoThere's nothing more fun than making a DSL, the only annoying part if finding an excuse to make one
- auggierose 8mo agoWhy "shadow" instead of "test"?
- thomasahle 8mo agoI'd rather see a programing language optimized for "few tokens". Something like toon, but for code.
- benob 8mo agoAn LLM targeting language and no token efficiency?
- tossandthrow 8mo agoI can't seem to find in the repo what optimizes this language for LLMs and benchmarks for why it works?
- cheriot 8mo agoThe author's answers are toward the bottom of the README, https://github.com/jordanhubbard/nanolang?tab=readme-ov-file#why-nanolang https://github.com/jordanhubbard/nanolang?tab=readme-ov-file...
- dysoco 8mo agoI understand the effort and it seems like a nice little language but wouldn't it make more sense to target already existing C--, QBE, LLVMIR or similar? There must be "simpler C" languages already which sounds more useful given that LLMs must've been trained on them.
- tromp 8mo agoI'm a bit saddened that a language with 29 keywords is now considered tiny...
- _flux 8mo agoThe required-test-per-function is sort of interesting. But it's not enforced that the test does anything useful, is it? So I wonder how exhausting would it be to write in a language that required, for all functions, that they are tested with 100% path coverage. Of course, this by itself wouldn't still be equivalent to proving the code, but it would probably point people to the corner cases of code quite rapidly. Additionally it would make it impossible to have code that cannot be tested with 100% path coverage due to static relationships within it, that are not (or cannot be) expressed in the type system, e.g. if (foo) { if (!foo) {..} }. And would such a language need to have some kind of dynamic dependency injection mechanism for mocking the tests?
- ares623 8mo agoWhere will the training data come from?
- Surac 8mo agoas i already wrote in an other comment. Where are the millions lines of code needed to train LLM in this Nanolang? LLM are like parrots. if you dont give them data to extract the statistic probability of the next word, you will not get any usefull output. LLM do not think, they can't learn without training data
- ako 8mo agoThat's an incorrect assumption. You can get quite far with some skill documents and some examples in combination with tools to compile and run your code. The LLM will train itself on the fly based on the feedback from these tools.
- phplovesong 8mo agoI dont get it. Why is this better for the slop than, say python?
- v3ss0n 8mo agoWould be very bad for LLMs , `fn` is bad , braces are bad and it won't be useful to do anything for a long time . So all the LLMs will pass.
- tricorn 8mo agoUse Forth to create Lisp, implement Tcl in Lisp, then create Smalltalk using Tcl, then build Forth in Smalltalk. But wait, I'm just getting started!
- runjake 8mo agoContext: This project is by the FreeBSD (co-)founder and former Apple engineering director, Jordan Hubbard. He is now a senior director at Nvidia, according to his public LinkedIn[1]. 1. https://www.linkedin.com/in/johubbard/ https://www.linkedin.com/in/johubbard/
- wiremine 8mo agoI like the creativity, but I'm not sure it's needed. I've been building a large-scale database system using Opus 4.5, and targeting Rust. It's not perfect, but the Rust compiler is so helpful that Opus has solved a lot of problems on its own. I have around 100,000 lines of code, and have completed some major refactoring. I am using a variation of spec-driven development.
- jkh99 8mo agoThis absolutely was NOT needed, just to be clear, it was just fun to do and, perhaps more importantly, it taught me a lot in the process of making it. I also have a few geometric 3D printed objects on my desk that I made with openscad as "printing challenges" and then beat my head against my 3D printers for hours trying to actually print them. They did not need to be printed, they serve no purpose other than to be aesthetically pleasing and educational. :)
- wendgeabos 8mo agoIt would be good to see evidence to support the claims: NanoLang solves three problems: LLM Code Generation - Unambiguous syntax reduces AI errors Testing Discipline - Mandatory tests improve code quality particularly the first.