A language complete enough to build everything in, that is also the best thing to adopt for one piece of something else.
Nexium compiles to native code through C, has automatic reference counting without a tracing collector, a machine-checked effect system that says whether a function allocates, blocks, or can panic, and a compiler that turns one source tree into a C library, a Python wheel, a Rust crate, an npm package, or a command line tool. The compiler is written in Nexium and builds itself from the C it emits.
The Topo
The route up the mountain, one pitch at a time: install, hello world, the REPL, a calculator, ownership, effects, a GUI, a neural network, shipping a library. Every program in it is run by the test suite.
The language reference
Every construct the compiler implements, with the rules for types, ownership, effects, binary patterns, compile time and C interop.
The standard library
Every module and function, generated from the doc comments in std/.
The specification
What Nexium promises: the document the conformance suite is written against, and what a version number means (stability, platforms).
Install#
Windows: the installer from the latest release installs nx, a bundled Zig toolchain, the standard library, the examples and the VS Code extension.
macOS and Linux:
curl -fsSL https://raw.githubusercontent.com/Londopy/nexium/main/installers/install.sh | sh
From source, with nothing but a C compiler (Zig on the PATH, or cc on macOS):
git clone https://github.com/Londopy/nexium
cd nexium
sh bootstrap/build.sh
The compiler is nx-out/bootstrap/nx2. More ways to install.
One file, every target#
/// A hash of the bytes, the FNV-1a way.
fn checksum(data: []u8) -> u32 export(c) {
var h: u32 = 2166136261
for b in data {
h ^= b as u32
h *%= 16777619
}
return h
}
artifact cabi { name = "hasher" }
artifact python { name = "hasher" }
artifact node { name = "hasher" }
$ nx ship hasher.nx
hasher.h hasher.dll hasher.lib # C, from the same source
python/hasher/ hasher-1.0-*.whl # pip install
node/ # npm install
export(c) puts a function on the boundary. Because the checker proved checksum cannot panic, it gets its natural C signature; a function that could panic returns a status and the host gets an error instead of an abort. Embedding has the whole story.
What it feels like#
import std.json
struct Point derive(Eq) { x: f64, y: f64 }
fn centroid(points: []Point) -> ?Point {
if points.len == 0 { return null }
var sx = 0.0
var sy = 0.0
for p in points { sx += p.x; sy += p.y }
let n = points.len as f64
return Point{ .x = sx / n, .y = sy / n }
}
fn main() -> !void {
let doc = try json.parse("[[0, 0], [4, 0], [4, 4], [0, 4]]")
var points = List(Point).new()
for i in 0..json.len(&doc) {
let item = json.at(&doc, i) orelse continue
let x = json.as_num(json.at(item, 0) orelse continue) orelse continue
let y = json.as_num(json.at(item, 1) orelse continue) orelse continue
points.append(Point{ .x = x, .y = y })
}
if let c = centroid(points[..]) { println("centroid ({}, {})", .{c.x, c.y}) }
}
- Ownership without a borrow checker.
List,StringandMapown their storage; passing one moves it, a function borrows its parameters, and the compiler tells you when you used something after it moved. - Effects are inferred, bounds are checked. Write
!allocatesor!panicson a signature and the compiler names the exact line, through any number of calls, that would break the promise. - Compile time is the same language.
comptimeruns the interpreter the REPL uses; tables, tests and embedded files are computed before the C compiler sees them. - Binary patterns.
<<len:16/little, payload:len*8, rest:bytes>>parses a protocol in onematch.
The tools in the box#
nx build, run, test, check, fmt, doc, repl, lsp, ship, size, leaks, effects, audit, refcounts, doctor: one binary, no dependencies beyond a C compiler. The language server is nx lsp, and the repository's editors/ has it wired into VS Code, Vim, Neovim, Helix, Zed, Emacs, Kate, JetBrains, Sublime Text, Notepad++ and nano (the install guide has the one-liners).
Where things are#
- The Topo, the guided route through the language.
- The language reference and the standard library.
- The specification, the roadmap and the decisions that shaped the language.
- Contributing, the changelog, release names.
- The source, on GitHub.