Early Access · v0.6.0

ML types.
Effects as capabilities.
Actors, compiled native.

March is a statically typed functional language for programs built from actors. It takes message passing and supervision from the BEAM and algebraic data types from ML, and compiles the result to a native binary through LLVM. Memory is managed by Perceus reference counting with in-place reuse, so there is no garbage collector and no pauses, and functional code runs at Rust-like speed.

Safety is the compiler's job, not the test suite's.

Side effects are part of the type: a module declares the capabilities it needs, so what any piece of code can touch is visible in its signature, bounded by what main was granted, and auditable across every dependency you pull in. Linear types make a resource that is used out of order, twice, or never a compile error. Refinement types reject a value a function cannot accept before it runs. A session-typed channel's protocol is checked on both sides, a crashed actor is restarted the way you declared, and a running node takes new code without a restart.

Capabilities

The whole program is bounded by main

main's capability parameter is the program's grant. Everything it reaches has to fit under it: every helper, every stdlib call, every dependency. Reach past it and the build fails, naming the chain that got there.

Three layers, one function

What, in what order, with what values

Capabilities say what resources code may touch. Linear types and typestate say how many times, and in what order, a resource is used. Refinement types say with what values a function may be called. They are independent, they compose on the same function, and all three fire at compile time.

Protocols

Typed conversations, two parties or twenty

Write the conversation once. Each role gets a typed module, so a send out of turn, a second receive, or a role that stops halfway does not compile; a runner connects the roles over the network, each its own program on its own machine.

Hot deploy

New code into a running node, gated by what it may do

forge deploy hot ships only the functions that changed; actors keep their state and migrate it in place. A patch that reaches for authority the running version did not have stops until you grant it by name, and the node's own policy can still refuse it.

Concurrency

Actor model, isolated and supervised

Share-nothing message-passing actors with supervision trees. A mutable buffer cannot be sent in a message, and process-global state such as a Vault table needs IO.Mut, so shared mutable state shows up in a module's signature.

Performance

Zero-allocation recursive transforms

FBIP rewrites uniquely-owned data in place, so transforms like this allocate nothing on the reuse path. The functional-style rewrite is automatic; no unsafe code required.

See benchmarks →

Benchmark

tree-transform(depth=20, 100 passes)

After pass 1, March allocates nothing — every node rewritten in-place. The OCaml, Rust and Elixir versions build a fresh tree each pass, so this measures in-place reuse against allocation, not March against hand-written mutation. Median of 10, Apple M3 Max, --opt 2, from bench/RESULTS.md (2026-07-24). March is slower on fib, binary-trees and list-ops there.

LanguageMedianAllocs/pass
Elixir (BEAM)2369 ms2M
OCaml (ocamlopt)3670 ms2M
Rust (rustc -O)4902 ms2M
March (FBIP)579 ms0 ✓

← Back

See how March feels to write.

Full playground — no install needed ↗
march — interactive
March REPL — type an expression and press Enter.
march>

Get started in minutes

One command installs on macOS and Linux.

1 — Install
2 — Open the interpreter