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.
What makes March different
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.
| Language | Median | Allocs/pass |
|---|---|---|
| Elixir (BEAM) | 2369 ms | 2M |
| OCaml (ocamlopt) | 3670 ms | 2M |
| Rust (rustc -O) | 4902 ms | 2M |
| March (FBIP) | 579 ms | 0 ✓ |
← Back
Try March
See how March feels to write.
Full playground — no install needed ↗Quick Start
Get started in minutes
One command installs on macOS and Linux.