I have been doing some experiments training and tuning language models. All the good stuff to do that is in Python. I got so sick of Python just core dumping all over the place, I literally made a new "front end" for it.
What would happen if I vibe-coded a language based solely on my hatred of Python?
Enter Nevla.
It's basically a wrapper around Python with proper error handling and typing. While I was at it, I decided to add some ML constructs as first class citizens.
import py "torch"
fn main() (error?) {
w := check torch.randn([784, 10], requires_grad: true)
x := check torch.randn([32, 784])
logits := check (x @ w)
print(check str(logits.shape))
return none
}
- Statically typed, whole program checked before any of it runs. A program that passes the checker cannot crash the process. Worst case is an error returned from
mainor a controlled runtime fault with a nevla stack and a nonzero exit. No panics, no core dumps. - Errors are values and handling is mandatory.
checkpropagates,v, err :=handles locally, silently dropping one is a compile error. You can still avoid ever dealing with an error by growing(error?)on every function andchecking your way up tomain, and that is strongly recommended against: it moves every failure to the top with no context and no recovery. Handle errors at the layer that can do something about them; propagate only when the caller owns the decision. - Option types (
T?) instead of nil, with flow narrowing:if err != nonegives you the narrowed value in that branch. - Go's copy model. Scalars, strings, and structs copy; lists, maps, functions, and py handles are references. Closures capture by reference.
- Embedded CPython, not a subprocess.
import py "torch"binds the real module. A chain of Python operations is one fallible unit:check model(x).loss.item()yields the value or the Python exception converted to a nevla error, with no per-step ceremony. Keyword args pass through (optim.Adam(params, lr: 0.001)),for rangeworks over any Python iterable, and you can assign into Python attributes and subscripts. - ML sugar:
@is matrix multiplication, dispatched to__matmul__. - Small stdlib:
error,math,file,ctx(cancellation handles: deadlines and SIGINT),http.
Split like uv and python. nevla does setup: nevla new, nevla py add torch, nevla check, nevla run. nv runs code: nv train.nv, and bare nv is the repl. Python deps live in the project manifest and every import py is validated against it at compile time, so a missing dep is a compile error, not a stack trace twenty minutes into a training run.
Try it without installing anything: the playground runs the interpreter in your browser (the py bridge needs a real CPython, so that part is native-only). The nevla book is the guide.
nevla ships as a python wheel carrying both binaries, so uv is the whole story:
uv tool install nevla
nevla new hello && cd hello
nevla run # hello, nevla
nevla py add numpy # declare a Python dep; uv builds .nevla/venv
nv src/main.nv # run a file directly; bare nv is the replHomebrew works too, same wheels underneath: brew install guygrigsby/tap/nevla.
New projects come with AGENTS.md, a nevla primer for coding agents; nevla new --claude-hook also installs a Claude Code hook that typechecks after every edit. nevla new only ever writes into the directory it creates; it refuses to run where anything already exists.
The gate is NEVLA_TEST_PY=1 cargo test, green before every commit (the py goldens need a python3 on PATH). Language behavior lives in tests/golden/: a .nv file next to a .out (expected stdout) or .err (expected error substrings), and a directory with a main.nv is one multi-file case. Any change to language semantics updates language-spec.md in the same commit, no exceptions. nevla fmt rewrites source in the one true style (--check for CI). The front end has a fuzz target, cargo +nightly fuzz run parse_check, and CI runs the full gate plus a 60 second fuzz pass on every push.
language-spec.md is the normative spec. tests/golden/ is the executable spec; every language-visible behavior has a golden test. Design rationale is in docs/specs/, decisions in docs/adr/. There's an nvim plugin under editors/ with syntax highlighting and check-on-save.
Nevla (नेवला) is Hindi for mongoose. The project was briefly named for Kipling's Rikki-Tikki-Tavi and renamed once the story's colonial subtext was pointed at directly; ADR 0014 and the book's mascot page give the full account. Same purple mongoose: the animal was never the problem.
