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"
// check propagates: the caller decides
fn logits(n int) (str, error?) {
w := check torch.randn([784, 10], requires_grad: true)
x := check torch.randn([n, 784])
y := check (x @ w)
return check str(y.shape), none
}
// main can act, so it handles
fn main() {
shape, err := logits(32)
if err != none {
print("torch failed: " + err.msg)
return
}
print("logits: " + shape)
}
- Statically typed, whole program checked before any of it runs. No crash originates in nevla: worst case is an error returned from
mainor a controlled runtime fault with a nevla stack and a nonzero exit, and every Python exception arrives as an error value. The one documented boundary: a C extension that itself segfaults takes the process down, as it would take down any host that loaded it. - 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,os,time(int nanoseconds, ctx-aware sleep),regex(RE2 flavor),flag,gpu,test.
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 rikki; ADR 0014 records the rename.
