Getting Started
This guide walks you from zero to a working March program. See Installation first if you haven’t installed March yet.
Hello, World
Every March file starts with a module declaration. Create hello.march:
mod Hello do
fn main() do
println("Hello, March!")
end
end
Run it:
march hello.march
(Building from source instead of using an installed binary? Use dune exec march -- hello.march.)
Output:
Hello, March!
What’s happening:
mod Hello do ... enddeclares a module. Every file must have exactly one top-level module.fn main() do ... endis the program entry point.printlnis a builtin that writes a line to stdout.
A More Complete Program
mod Greet do
fn greet(name : String) : String do
"Hello, " ++ name ++ "!"
end
fn main() do
let message = greet("World")
println(message)
let names = ["Alice", "Bob", "Carol"]
List.each(names, fn n -> println(greet(n)))
end
end
Run it:
march greet.march
(Source build: dune exec march -- greet.march.)
Output:
Hello, World!
Hello, Alice!
Hello, Bob!
Hello, Carol!
Key things to notice:
let x = exprbinds a name (noinneeded; subsequent lines in the block see it)++concatenates stringsList.eachtakes a list and a function, running it for its side effects- Lambdas are written
fn x -> body
Compiling to a Binary
To produce a standalone native binary, use --compile:
march --compile -o greet greet.march
./greet
(Source build: dune exec march -- --compile -o greet greet.march.)
The compiler runs LLVM, links the C runtime, and produces a native executable.
The REPL
Start an interactive session:
march repl
(march with no arguments also drops you into the REPL. Building from source:
dune exec march -- repl.)
Or via forge:
forge interactive
(Source build: dune exec forge -- interactive.)
The REPL loads the standard library and drops you into a numbered prompt:
march(1)>
Try some expressions:
march(1)> 1 + 1
= 2
march(2)> "Hello" ++ " " ++ "March"
= "Hello March"
march(3)> let xs = [1, 2, 3, 4, 5]
val xs = [1, 2, 3, 4, 5]
march(4)> xs |> List.map(fn x -> x * x)
= [1, 4, 9, 16, 25]
The last result is always bound to v:
march(5)> 42 * 2
= 84
march(6)> v + 1
= 85
Run :set +t to also print each result’s inferred type (:set -t to turn it
back off) — see the REPL guide for the full command list.
Using forge
forge is the recommended project manager for anything beyond a single file.
Create a new project:
forge new my_app
cd my_app
(Source build: dune exec forge -- new my_app.)
This scaffolds:
my_app/
├── forge.toml # project manifest
├── src/
│ └── my_app.march # entry point
└── test/
└── my_app_test.march
Build and run:
forge build
forge run
Run tests:
forge test
(Source build: prefix each with dune exec forge --, e.g. dune exec forge -- build.)
Program Structure
A typical March program has:
mod MyApp do
-- Type definitions
type Color = Red | Green | Blue
-- Pure helper functions
fn color_name(c : Color) : String do
match c do
Red -> "red"
Green -> "green"
Blue -> "blue"
end
end
-- Entry point
fn main() do
let c = Green
println("Color: " ++ color_name(c))
end
end
The main() function is called automatically when the program starts. Its return type can be Unit (implicit) or Int for an exit code.
Type Annotations
Type annotations are optional but useful for documentation and catching mistakes early:
fn add(x : Int, y : Int) : Int do
x + y
end
Without annotations, the compiler infers everything:
fn add(x, y) do
x + y
end
Both are valid. The compiler will catch type errors either way.
Next Steps
Ready to build something? → Build a CLI Tool takes you from forge new to a working binary, and the Cookbook has goal-oriented recipes (CLI, HTTP, JSON, files, config).
Learn the language:
- Language Tour — a comprehensive walkthrough of all syntax
- Type System — algebraic data types, generics, Option/Result
- Pattern Matching — destructuring and exhaustiveness checking
- Actors — concurrent programming with the actor model, and the jumping-off point for supervision, clustering, and hot code reload
Curious about March’s compile-time safety guarantees? These go beyond a standard ML-family type system:
- Interfaces — ad-hoc polymorphism with
interface/impl - Linear Types — resources the compiler proves are used exactly once, at zero runtime cost
- Refinement Types — value predicates (
{Int | _ >= 0}) checked by an SMT solver - Capabilities — IO permissions tracked in the type system
- Safety by Construction — how these layers compose on one function
- Memory Model — why March has no garbage collector or pauses
Coming from another language? Python · TypeScript · Haskell/Elixir/OCaml.