v0.2.5 rex Faster dev builds and stack-safe codecs What's new

Say it in slang.
Ship it as C.

A language made for AI agents and humans.

The whole language fits in about 3,000 tokens of context. Errors say the fix, and passing tests print one line, so your agent reads less, guesses less and retries less. Your servers compile through C into one small native binary.

tokens slang vs Go vs TypeScript, same tasks, same agent: benchmark in progress vs Go and TypeScript: benchmark in progress
~3k tokens
to read the whole language. One page your agent can hold.
2.9 MB
peak memory serving 39.9k req/s. Go: 9.6 MB. Node: 185 MB.
1.5× Go
on the CPU and allocation benchmark. Second only to Rust.

Light tier of the cross-language suite, 4-vCPU VM. Every run, including the ones we lose →

Try it

Read it once. Press run.

Six small programs and what they print. Short enough to fit in a prompt, strict enough that a mistake shows up on the first run.

fn fib(n: int) -> int {    if n < 2 { return n; }    return fib(n - 1) + fib(n - 2);} for k in 1..=3 {    println("tick ${k}");}println("fib(20) = ${fib(20)}");
~/hello $ slangc hello.sl --run
tick 1tick 2tick 3fib(20) = 6765[exit 0]

Functions, ranges and string interpolation.

Recorded with slangc 0.2.5. CI compiles each one and checks its output against the real compiler.

Performance

Small is the feature.

Measured against eight languages on the same contract, with a conformance check before anything is timed. Memory is where slang pulls away. Throughput sits in the pack.

Peak memory, HTTP server at 50 connections lower is better
  1. slang2.9 MB
  2. Rust3.2 MB
  3. Go9.6 MB
  4. C#40.0 MB
  5. Java125.6 MB
  6. Bun146.0 MB
  7. Node.js184.6 MB
CPU and allocation, 1,000 tasks wall time, lower is better
languagemsRSS
Rust1,49810.1 MB
slang1,75713.3 MB
Bun1,78588.4 MB
Node.js1,80181.1 MB
Java1,99072.5 MB
C#2,07241.7 MB
Go2,6378.3 MB
Throughput, raw socket layer

39,857 req/s

At 50 connections: Go 38,439, Rust 38,021. At 200, Go edges ahead, 36,492 to 35,553. Close enough that a laptop's noise would swap them.

  • 3Mdatabase rows streamed in under 20 MB
  • ~0.8 msper minor collection, whether 20k or a million objects are live
  • 0.53 sfor four 500 ms HTTP/2 requests on one connection

Light tier of bench/suite: raw-socket responders and a compute job on a shared 4-vCPU VM, median of 3 rounds. The Postgres and JSON heavy tier has not run yet. Full record and method →

Concurrency

Park the task, not the thread.

spawn puts a green task on a pool with one worker per core. Sockets, sleeps, channels and locks park the task and hand the thread back. There is no async, no await and no second colour of function: code that looks blocking stays simple.

  • 10,000 tasks that each sleep 100 ms finish in about 0.6 s on a laptop.
  • Preempted when a task spins past its slice, so one hot loop cannot starve a worker.
  • TLS and DNS park too. Handshakes run on the reactor; lookups on their own thread.
  • A precise, generational GC with a write barrier and a verifier mode in CI.
How the scheduler works →
tasks: parked, cost a queue slot run queues worker threads, one per core cpu 0cpu 1cpu 2cpu 3
Thousands of parked tasks, a handful of threads. A task that wakes goes back on a queue; a free worker picks it up.

Built for agents

Cheap to learn. Cheap to correct.

An agent spends its tokens reading docs, choosing between options and retrying after errors. slang is designed to cut all three, for the agent and for whoever reviews its work.

Learn it in one read

llms-small.txt is the language and its packages on one page, about 3k tokens. Every example in it compiles in CI. For a single API, ask the compiler.

just the signatures you need
~/api$ slangc doc regex
package regex (native)

fn compile(str) -> result[rawptr,str]
fn is_match(rawptr, str) -> bool
fn find(rawptr, str) -> [int]
fn find_at(rawptr, str, int) -> [int]

Errors that say the fix

A compile error names the construct, the line and what to write instead. A runtime error names the function and the values, so the next attempt is the right one.

one turn to fix, not five
~/orders$ slangc main.sl
main.sl:1: error: cannot infer the element type of an empty list; annotate it, e.g. let xs: [int] = []

~/orders$ ./orders
slang runtime error: list index out of bounds at orders.line_total:2 (index 7, length 3)

Quiet when it works

Tool output an agent can afford: one line when the tests pass, and only the failures when they do not.

no wall of green
~/math$ slangc test .
ok: 2 passed (155us)

~/math$ slangc test .
FAIL test_add_neg (44us)
     assertion failed at math.test_add_neg:2
FAIL: 1 of 2 failed (96us)
First run pending

The claim, measured in public.

Five backend tasks go to the same agent three times over: in slang with zokor, in Go with Fiber, and in TypeScript with NestJS. Hidden black-box tests decide what passes. We count input tokens, output tokens and turns until they do, and publish the runs where slang still wastes them.

The harness, specs and tests →
  1. notesCRUD with validation
  2. authLogin, bearer tokens, middleware, logout
  3. jobsA background worker with concurrency
  4. ratelimitPer-client limits with Retry-After
  5. uploadMultipart, size limits, safe file names

zokor

One way to do each thing.

zokor is the backend framework for slang. It makes the decisions every service otherwise makes differently, so neither an agent nor a new hire has to.

routes.sl
r.get("/health", health);            // public

let api = r.group("/api/v1");
api.before(require_token);           // everything below is guarded
api.get("/orgs/:id", show_org);
api.post("/orgs", create_org);

fn show_org(c: zokor.Ctx[App]) -> http.Response {
    guard let org = find_org(c.state.db, c.param("id")) else {
        return zokor.respond(c.state.errors, "org.not_found", c.request_id);
    }
    return zokor.ok_json(encode(org));
}

Every choice an agent faces costs reasoning tokens. zokor answers them once.

Which router?
Router[S], generic over your state
What does an error look like?
One JSON envelope, from a registry
Where is config read?
.env, then the environment. Every problem at once
How is input validated?
DTO checks, as field-level errors
How are uploads made safe?
Size limits, type allowlists, cleaned names
WebSockets?
RFC 6455, and Socket.IO
zokor on GitHub →

The language

Familiar on purpose. Small on purpose.

Syntax you already read

fn, guard let, switch, chan: shapes from Go and Swift, because people and models already know them. Every novelty costs someone a retry.

fn abs(n: int) -> int {
    guard n >= 0 else {
        return -n;
    }
    n
}

let code = 404;
switch code {
  case 200, 201 { println("ok"); }
  case 404 { println("missing"); }
  default { println("other"); }
}

Left out, deliberately

Each missing feature is one less way for a program to surprise you.

No null
opt[T], unwrapped with guard let or ??
No exceptions
result[T, E] for bad data, fault for a bad world
No closures
A function value is a C function pointer. Nothing is captured
No interfaces, no reflection
Generics, checked for each type they are used with
No async, no await
spawn, and blocking calls that park

C is the runtime, not a foreign country

slang emits C and hands it to your cc. Calling a C library is extern fn and a linker flag: no bindings generator, no marshalling layer, no second build system.

link "sqlite3";

extern fn sqlite3_libversion() -> str;
println(sqlite3_libversion());
C interop →

Standard library

Batteries inside the compiler.

27 packages ship with slangc. Native ones are linked only when a program imports them, so a plain TCP server stays free of OpenSSL, SQLite and zlib.

Get started

From clone to hello.

Build the compiler from source, then start a project. make install puts slangc and its runtime under /usr/local; PREFIX=~/.local works too.

What you need
AnythingA C compiler
net TLS, crypto, httpsOpenSSL and pkg-config
sqlSQLite
compress, httpczlib

CI builds and tests every merge on Linux x86_64 and arm64 (GCC) and macOS on Apple Silicon (clang).

terminal
~$ git clone https://github.com/dolphlabs/slang && cd slang
~/slang$ sudo make install
~/slang$ slangc new hello && cd hello
~/hello$ slangc main.sl --run
hello from hello

FAQ

Asked, answered.

Is slang ready for production?

It is young: v0.2, and the language can still change. The runtime is tested on three platforms on every merge, including a collector verifier and stress runs under forced preemption. Read Known limitations before you bet a service on it, and pin a commit when you do.

Is it faster than Go or Rust?

On what has been measured so far: it uses less memory than Go, finishes the CPU and allocation job in two thirds of Go's time, and trails Rust. Raw HTTP throughput is within a few percent of both. The heavier database-and-JSON tier has not run yet. The benchmarks page shows every number, including the ones slang loses.

Why compile to C?

C runs everywhere a server does, its compilers have decades of optimisation in them, and every C library is one extern fn away. slangc emits one C file with the runtime spliced in and builds it with cc -O3 -flto.

Why no closures, exceptions or null?

Each is a place where control flow or ownership hides. Without closures a function value is exactly a C function pointer and the collector can ignore it. Without exceptions every failure is a value you can see in the signature. Without null, absence is opt[T] and the compiler makes you handle it.

What does "cheap for agents" mean, and is it proven?

It means fewer tokens to learn the language, fewer choices to reason about, and errors that lead to the fix in one turn. It is a design goal, not a result yet: the agent benchmark above is built and published, and its first run is pending.

Can I call existing C libraries?

Yes: extern fn plus link "name". Numbers, bool, str, bytes and opaque pointers cross the boundary; GC-managed values are rejected at compile time, so C never sees their layout.

Who builds slang?

Dolphlabs (Dolph Tech Limited), in the open, under the MIT license.

Hop in.

Say it in slang. Ship it as C.