Open-source project
betaveros/noulith avatar
betaveros/noulith

Noulith: a language where every operator is infix

*slaps roof of [programming language]* this bad boy can fit so much [syntax sugar] into it

1,168 stars21 forksRustLicense varies

At a glance

What is it?
Noulith is an experimental language written in Rust, built by one developer to practise implementing a language and to have a fast scripting tool with a REPL. Its defining idea is that everything is an infix operator and that operator precedence is resolved at runtime, so a running expression can be re-parsed by swapping two operators.
Who is it for?
Noulith fits a developer who wants to read a working language implementation, to try an unusual parsing model, or to run a small script on a machine with no package manager, and it fits as a playground for ideas about operator design. It does not fit production work, because the README says the language will keep changing and the documentation will drift, and because the repository carries no licence file, which leaves the terms for reuse unstated.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One developer, one language, two purposes

Noulith is described as an attempt to give the author a new Pareto-optimal choice for quick and dirty scripts, particularly on a machine that is not a development computer, and as practice in writing a more realistic programming language after spending too long on an overengineered stack-based project. Both motives are visible in the design. The scripts use case explains the lightweight installation, the REPL and the compiled binary with no runtime to install. The language project explains the features that have no business in a practical language, such as runtime operator precedence and prefix operators that behave oddly enough that the README advises parenthesising them. The author also notes that the project is a response to a book on implementing interpreters, which is the right frame for reading the code. There is a WebAssembly build, so the language can be tried in a browser without installing anything.

Everything is an infix operator

The central claim is that everything is an infix operator and that nearly everything can be partially applied, so pipelines read as a chain of named operations:

code
noulith> 1 to 10 filter even map (3*)
[6, 12, 18, 24, 30]

The range produces one to ten, the filter keeps the even numbers, and the map applies a partially applied multiplication by three, which is why the operator sits in parentheses. A practical consequence shows up in ordinary code. The author wanted to write a maximum-tracking assignment inside a complicated loop, which in most languages needs a temporary variable and a comparison, and here it becomes an operator of its own that can be used with literally any function. The same design also removes a familiar inconsistency, because Noulith does not distinguish sets from dictionaries, so there is no empty brace that means one thing in one place and another thing in another.

Precedence that is decided when the expression runs

The second unusual decision is that operator precedence is not fixed by the language but resolved at runtime, and the REPL session in the README shows exactly what that means:

code
noulith> f := \-> 2 + 5 * 3
noulith> f()
17
noulith> swap +, *
noulith> f() # (2 times 5) plus 3
13
noulith> swap +::precedence, *::precedence
noulith> f() # 2 times (5 plus 3)
16
noulith> swap +, *
noulith> f() # (2 plus 5) times 3
21

The same source text produces a different result after the operators are exchanged, because the parser re-reads the expression under the new precedence rules. The author frames the appeal as the amount of operator parsing code nobody has to write, and he is explicit about the condition: it suits the case where you need arbitrarily many levels of precedence and are happy to evaluate inputs. A scripting language that reads configuration or user input is exactly that case, and it is also a fairly large amount of machinery to trust in a system that is not otherwise used for critical work.

The numeric tower and the type rules

Noulith is dynamically typed, and its numbers follow a coercion ladder: big integers, rationals, floats and complex numbers, coercing from left to right in that order as needed. Division is exact, behaving like the rational arithmetic in Common Lisp, while the modulo operator keeps the signed C behaviour, and a separate operator does integer division rounding down. Exponentiation has its own symbol, and bitwise exclusive or is spelled differently from the usual C convention, with a word form as an alternative. Radix literals are supported up to base 36 with a syntax such as 36r1000, including a base 64 spelling for a case the author finds funny. There are no booleans, since 0 and 1 do that job, and comparisons chain the way they do in Python, with a three-valued comparison operator and its reverse alongside the usual min and max functions.

Syntax where everything is an expression

The surface syntax is a mixture of influences, and the README is candid about which parts are unstable. Variables are declared with :=, a choice the author attributes to a design note in the interpreter book he read, and every statement is an expression, including the if and else construct, which at the top level is the C style conditional and doubles as the ternary expression. Loops use a left arrow to iterate values and a double-headed arrow for index and value or key and value pairs. Lambdas are written with a backslash and an arrow, list concatenation uses ++, string concatenation uses a dollar sign, and the author marks that one as maybe, not sure yet. There are no classes or members, only global functions, and scopes come from functions, loops, switch and try. Newlines carry no meaning, semicolons appear everywhere, and the author can foresee regretting that choice.

Building the interpreter and the REPL

The project is a standard Rust project, and the README gives the whole workflow: install Rust, clone the repository, and run the binary with three optional features enabled:

bash
cargo run --release --features cli,request,crypto

That drops you into a REPL, and a filename can be passed instead to run a script. Building a standalone executable follows the same shape, with cargo build --release and the binary appearing under target/release, ready to be aliased or added to the path. The features are the interesting part of the manifest: cli brings in the rustyline line editor and a directory helper for the REPL, request adds an HTTP client, crypto adds several hash and cipher implementations, and a fourth feature, parallel, wires in a work pool. The build is not cheap by default, and the README notes that without the release profile the stack frames are so large that one of the tests overflows the stack. The crate also builds for the wasm32 target, which is how the browser version exists.

What the repository does not promise

The README carries a warning at the top of its feature list, saying that the claims will become false as the author keeps hacking on the project, and later notes that the language will probably keep changing and that the documentation may not stay up to date. That honesty is useful, but it also defines the project. There are no GitHub releases, the version in the manifest is 1.2.0, and the tree contains no licence file, so the terms for reuse are not stated anywhere in the repository. The examples directory holds the kind of programs that demonstrate the design, including a deque implementation and several FizzBuzz variants in imperative, functional and hyperfunctional styles, and there is a separate noulib directory and a BUILTINS.md document. The right expectation is a research project with a working implementation, not a dependency.

Noulith next to the languages it borrows from

The design compares itself to Lisp, Haskell, Scala and Python, and the differences are structural rather than cosmetic. Against Lisp, Noulith adds infix operators and partial application to a syntax that is otherwise expression oriented, and against Haskell it keeps the operator symbols strung together freely but resolves precedence dynamically instead of statically. Python is the comparison that matters most for the scripting goal, since the author set out to avoid its aliasing and comprehension noise, offering nested literal construction and mutation through copy-on-write instead. What none of those languages do is move precedence to runtime, and that single decision is both the novelty and the cost, because a reader who assumes familiar precedence rules will mispredict results until they learn to check the current table.

Editorial conclusion

Noulith fits a developer who wants to read a working language implementation, to try an unusual parsing model, or to run a small script on a machine with no package manager, and it fits as a playground for ideas about operator design. It does not fit production work, because the README says the language will keep changing and the documentation will drift, and because the repository carries no licence file, which leaves the terms for reuse unstated. Before running it, read the README end to end rather than the summaries, and if you intend to build on the code, settle the licence question with the author first, because nothing in the repository answers it.

Frequently asked questions

How do I run Noulith locally?

Install Rust, clone the repository and run cargo run --release --features cli,request,crypto. That opens a REPL, or you can pass a filename to run a script, and cargo build --release with the same features produces a binary under target/release.

What is unusual about operators in Noulith?

Everything is an infix operator, nearly everything can be partially applied, and operator precedence is resolved at runtime, so a running expression can be re-parsed by exchanging two operators with the swap command.

Which number types does Noulith support?

Big integers, rationals, floats and complex numbers, coercing from left to right as needed. Division is exact rational arithmetic, there are no booleans because 0 and 1 are used instead, and radix literals work up to base 36.

Does Noulith have classes or object orientation?

No. The README states there are no classes or members, only global functions, with scopes introduced by functions, loops, switch and try, and that every statement is an expression.

Can I try Noulith without installing Rust?

Yes. The project publishes a WebAssembly build that runs in the browser, and the crate compiles for the wasm32 target with wasm-bindgen and js-sys.

Is Noulith suitable for production use?

Not on the evidence in the repository. The README warns that the language will keep changing and the documentation may drift, there are no tagged releases, and the tree contains no licence file, so the terms for reuse are unstated.

Official sources

  1. betaveros/noulith on GitHub
  2. Issues
  3. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/betaveros-noulith.svg)](https://hysenlabs.com/projects/betaveros-noulith)

Was this useful?