Standard Library

March ships with 57 stdlib modules covering collections, strings, I/O, HTTP, cryptography, and more. This page provides an overview and quick reference for the most commonly used modules.

All stdlib modules are available without any import statement — use qualified access (List.map, String.length, etc.) or import/use to bring names into scope.


Prelude (Auto-imported)

The Prelude module is automatically imported into every March program. These names are always in scope:

-- Diverging
panic("invariant violated")   -- terminates with an error
todo("not yet implemented")   -- typechecks as any type
unreachable()                 -- asserts a branch can't be reached

-- Option helpers
unwrap(Some(42))              -- 42, panics if None
unwrap_or(None, 0)            -- 0
unwrap_or_else(opt, fn () -> compute_default())

-- List basics
head([1, 2, 3])               -- 1, panics if empty
tail([1, 2, 3])               -- [2, 3], panics if empty
is_nil([])                    -- true

-- Combinators
identity(x)                   -- x
compose(f, g)                 -- fn x -> f(g(x))
flip(f)                       -- fn a b -> f(b, a)
const(x)                      -- fn _ -> x

List

list.march — 508 lines. The standard singly-linked list.

-- Construction
List.empty()                         -- []
List.singleton(42)                   -- [42]
List.repeat("hi", 3)                 -- ["hi", "hi", "hi"]
List.range(0, 5)                     -- [0, 1, 2, 3, 4]
List.range_step(0, 10, 2)            -- [0, 2, 4, 6, 8]

-- Access
List.head([1, 2, 3])                 -- 1  (panics if empty)
List.head_opt([1, 2, 3])             -- Some(1)
List.last([1, 2, 3])                 -- 3
List.nth([10, 20, 30], 1)            -- 20  (0-indexed)
List.nth_opt([10, 20], 5)            -- None

-- Predicates
List.is_empty([])                    -- true
List.length([1, 2, 3])               -- 3
List.any([1, 2, 3], fn x -> x > 2)  -- true
List.all([2, 4, 6], fn x -> x % 2 == 0)  -- true
List.member(2, [1, 2, 3])           -- true

-- Transformation
List.map([1, 2, 3], fn x -> x * 2)          -- [2, 4, 6]
List.filter([1, 2, 3, 4], fn x -> x % 2 == 0) -- [2, 4]
List.filter_map(xs, fn x -> if x > 0 do Some(x * 2) else None end)
List.flat_map([1, 2, 3], fn x -> [x, x * 10])  -- [1, 10, 2, 20, 3, 30]
List.reverse([1, 2, 3])              -- [3, 2, 1]
List.append([1, 2], [3, 4])         -- [1, 2, 3, 4]
List.concat([[1, 2], [3], [4, 5]])   -- [1, 2, 3, 4, 5]

-- Folds
List.fold_left(0, [1, 2, 3], fn acc x -> acc + x)   -- 6
List.fold_right([1, 2, 3], 0, fn x acc -> x + acc)  -- 6
List.scan_left(0, [1, 2, 3], fn acc x -> acc + x)   -- [0, 1, 3, 6]

-- Search
List.find([1, 2, 3], fn x -> x > 1)      -- Some(2)
List.find_index([10, 20, 30], fn x -> x == 20)  -- Some(1)

-- Iteration (side effects)
List.iter([1, 2, 3], fn x -> println(int_to_string(x)))

-- Sorting
List.sort_by([3, 1, 2], fn a b -> a < b)   -- [1, 2, 3]

-- Zipping
List.zip([1, 2, 3], ["a", "b", "c"])  -- [(1, "a"), (2, "b"), (3, "c")]
List.unzip([(1, "a"), (2, "b")])       -- ([1, 2], ["a", "b"])
List.with_index([10, 20, 30])          -- [(0, 10), (1, 20), (2, 30)]

-- Grouping
List.intersperse([1, 2, 3], 0)         -- [1, 0, 2, 0, 3]
List.take(3, [1, 2, 3, 4, 5])         -- [1, 2, 3]
List.drop(2, [1, 2, 3, 4])            -- [3, 4]
List.take_while([1, 2, 3, 4], fn x -> x < 3)   -- [1, 2]
List.drop_while([1, 2, 3, 4], fn x -> x < 3)   -- [3, 4]
List.chunk_by([1, 1, 2, 2, 3], fn a b -> a == b)  -- [[1, 1], [2, 2], [3]]

String

string.march — String operations.

String.length("hello")              -- 5
String.concat(["a", "b", "c"])      -- "abc"
String.join(["a", "b", "c"], ", ")  -- "a, b, c"
String.split("a,b,c", ",")          -- ["a", "b", "c"]
String.trim("  hello  ")            -- "hello"
String.trim_left("  hi")            -- "hi"
String.trim_right("hi  ")           -- "hi"
String.upcase("hello")              -- "HELLO"
String.downcase("HELLO")            -- "hello"
String.starts_with("hello", "he")   -- true
String.ends_with("hello", "lo")     -- true
String.contains("hello world", "world")  -- true
String.replace("hello", "l", "r")   -- "herro"
String.slice("hello", 1, 3)         -- "el"
String.to_int("42")                 -- Some(42)
String.to_float("3.14")             -- Some(3.14)
String.repeat("ab", 3)              -- "ababab"
String.reverse("hello")             -- "olleh"
String.chars("hi")                  -- ["h", "i"]

Map

map.march — HAMT-backed persistent hash map. O(1) amortized operations.

-- Construction
let m = Map.empty()
let m2 = Map.singleton("key", 42)
let m3 = Map.from_list([("a", 1), ("b", 2), ("c", 3)])

-- Access
Map.get(m3, "a")         -- Some(1)
Map.get_or(m3, "z", 0)  -- 0
Map.contains_key(m3, "b")  -- true
Map.size(m3)             -- 3
Map.is_empty(m)          -- true

-- Modification (returns new map)
let m4 = Map.insert(m3, "d", 4)
let m5 = Map.remove(m4, "a")

-- Traversal
Map.keys(m3)             -- ["a", "b", "c"] (in some order)
Map.values(m3)           -- [1, 2, 3]
Map.entries(m3)          -- [("a", 1), ("b", 2), ("c", 3)]
Map.fold(0, m3, fn acc k v -> acc + v)   -- 6

-- Transformation
Map.map_values(m3, fn v -> v * 10)   -- {"a": 10, "b": 20, "c": 30}
Map.filter(m3, fn k v -> v > 1)      -- {"b": 2, "c": 3}
Map.merge(m3, m4)                    -- right takes precedence on conflict
Map.merge_with(m3, m4, fn a b -> a + b)  -- custom merge function

-- Converting
Map.to_list(m3)   -- [("a", 1), ("b", 2), ("c", 3)]

Set

set.march — HAMT-backed persistent set.

let s = Set.from_list([1, 2, 3, 4, 5])
Set.member(3, s)           -- true
Set.insert(6, s)           -- {1,2,3,4,5,6}
Set.delete(3, s)           -- {1,2,4,5}
Set.size(s)                -- 5
Set.union(s1, s2)
Set.intersection(s1, s2)
Set.difference(s1, s2)
Set.to_list(s)             -- [1, 2, 3, 4, 5]
Set.filter(s, fn x -> x % 2 == 0)  -- {2, 4}

Option

option.marchOption(a) = None | Some(a).

Option.map(Some(5), fn x -> x + 1)     -- Some(6)
Option.map(None, fn x -> x + 1)        -- None
Option.and_then(Some(5), fn x -> if x > 3 do Some(x) else None end)
Option.or(None, Some(42))              -- Some(42)
Option.or_else(None, fn () -> Some(0)) -- Some(0)
Option.unwrap_or(None, 0)              -- 0
Option.unwrap_or_else(None, fn () -> compute())
Option.is_some(Some(1))               -- true
Option.is_none(None)                  -- true
Option.to_list(Some(42))              -- [42]
Option.to_list(None)                  -- []
Option.filter(Some(5), fn x -> x > 3)  -- Some(5)
Option.filter(Some(2), fn x -> x > 3)  -- None

Result

result.marchResult(a, e) = Ok(a) | Err(e).

Result.map(Ok(5), fn x -> x + 1)      -- Ok(6)
Result.map(Err("oops"), fn x -> x + 1) -- Err("oops")
Result.map_err(Err("x"), String.upcase) -- Err("X")
Result.and_then(Ok(5), fn x -> Ok(x * 2))   -- Ok(10)
Result.or(Err("x"), Ok(0))            -- Ok(0)
Result.unwrap(Ok(42))                 -- 42  (panics on Err)
Result.unwrap_or(Err("e"), 0)         -- 0
Result.is_ok(Ok(1))                   -- true
Result.is_err(Err("e"))               -- true
Result.to_option(Ok(42))              -- Some(42)
Result.to_option(Err("e"))            -- None

IO

io.march — Explicit I/O operations.

IO.puts("Hello, World!")         -- print with newline
IO.write("no newline")           -- print without newline
IO.warn("warning message")       -- print to stderr
IO.read_line()                   -- read a line from stdin -> String
IO.gets("> ")                    -- print prompt, read line
IO.inspect(any_value)            -- pretty-print any value with type info

The println and print builtins are also always available.


Math

math.march — Mathematical functions.

Math.abs(-5)          -- 5
Math.abs_f(-3.14)     -- 3.14
Math.min(3, 5)        -- 3
Math.max(3, 5)        -- 5
Math.clamp(15, 0, 10) -- 10
Math.sqrt(16.0)       -- 4.0
Math.pow(2.0, 10.0)   -- 1024.0
Math.exp(1.0)         -- 2.718...
Math.log(Math.e)      -- 1.0
Math.log2(8.0)        -- 3.0
Math.log10(1000.0)    -- 3.0
Math.floor(3.7)       -- 3.0
Math.ceil(3.2)        -- 4.0
Math.round(3.5)       -- 4.0
Math.sin(Math.pi /. 2.0)   -- 1.0
Math.cos(0.0)              -- 1.0
Math.pi                    -- 3.14159...
Math.e                     -- 2.71828...
Math.infinity              -- Float infinity
Math.is_nan(0.0 /. 0.0)   -- true

Crypto

crypto.march — Cryptographic primitives.

Crypto.sha256("hello")              -- hex string
Crypto.sha512("hello")              -- hex string
Crypto.hmac_sha256(key, message)    -- hex string
Crypto.random_bytes(32)             -- List(Int) of random bytes
Crypto.random_hex(16)               -- random hex string (32 chars)
Crypto.base64_encode(bytes)         -- Base64 string
Crypto.base64_decode(s)             -- List(Int)
Crypto.secure_compare(a, b)         -- constant-time equality
Crypto.pbkdf2_sha256(password, salt, iterations, key_len)  -- derived key

UUID

uuid.march — UUID generation and parsing.

UUID.v4()                     -- generate a random UUID string
UUID.v5(namespace, name)      -- deterministic UUID from namespace+name
UUID.parse("550e8400-...")    -- Option(UUID)
UUID.to_string(uuid)          -- "550e8400-e29b-41d4-a716-446655440000"
UUID.version(uuid)            -- 4
UUID.is_valid("550e8400-...") -- true
UUID.nil()                    -- "00000000-0000-0000-0000-000000000000"

JSON

json.march — JSON encoding and decoding.

type JsonValue =
  | JNull
  | JBool(Bool)
  | JInt(Int)
  | JFloat(Float)
  | JString(String)
  | JArray(List(JsonValue))
  | JObject(List((String, JsonValue)))

JSON.parse("{\"key\": 42}")          -- Result(JsonValue, String)
JSON.encode(JObject([("x", JInt(1))]))  -- "{\"x\":1}"
JSON.encode_pretty(val)               -- pretty-printed JSON
JSON.get(obj, "key")                  -- Option(JsonValue)

HTTP Client

http_client.march — Make HTTP requests.

let resp = Http.get("https://api.example.com/data")
let resp = Http.post("https://api.example.com/data", body)
let resp = Http.request({
  method  = "PUT",
  url     = "https://api.example.com/items/1",
  headers = [("Content-Type", "application/json")],
  body    = Some(json_body)
})

match resp do
  Ok(r) ->
    println("status: " ++ int_to_string(r.status))
    println("body: " ++ r.body)
  Err(e) ->
    println("error: " ++ e)
end

File System

file.march, dir.march, path.march:

-- File I/O (Result-based)
File.read("data.txt")              -- Result(String, String)
File.write("out.txt", "content")   -- Result((), String)
File.append("log.txt", "line\n")   -- Result((), String)
File.exists("config.json")         -- Bool
File.delete("temp.txt")            -- Result((), String)
File.copy("src.txt", "dst.txt")    -- Result((), String)

-- Directory
Dir.list("./src")                  -- Result(List(String), String)
Dir.create("./output")             -- Result((), String)
Dir.exists("./data")               -- Bool

-- Path manipulation (pure, no I/O)
Path.join("src", "main.march")     -- "src/main.march"
Path.dirname("src/lib/foo.march")  -- "src/lib"
Path.basename("src/lib/foo.march") -- "foo.march"
Path.extension("foo.march")        -- ".march"
Path.stem("foo.march")             -- "foo"
Path.is_absolute("/usr/bin")       -- true

System

system.march — OS and runtime information.

System.os()                -- "macos" | "linux" | "windows"
System.arch()              -- "x86_64" | "arm64"
System.cpu_count()         -- number of logical CPUs
System.monotonic_time()    -- Int (nanoseconds, for timing)
System.env("HOME")         -- Option(String)
System.put_env("KEY", "val")
System.argv()              -- List(String)
System.cwd()               -- String
System.pid()               -- Int (OS process ID)
System.exit(0)             -- terminate with exit code
System.cmd("ls", ["-la"])  -- Result(String, String)

Logger

logger.march — Structured logging.

Logger.info("server started")
Logger.warn("connection retry")
Logger.error("database unreachable")
Logger.debug("processing request")
Logger.with_context(fn () ->
  Logger.put_context("request_id", "abc123")
  Logger.info("request received")
  process_request()
)

Vault

vault.march — Process-local key-value store backed by a mutable hash table. Used extensively in the stdlib for global mutable state.

Vault.put("counter", 0)
Vault.get("counter")          -- Option(a)
Vault.update("counter", fn n -> n + 1)
Vault.delete("counter")
Vault.keys("counter_")        -- List(String) with prefix
Vault.all()                   -- List((String, a))

Enum

enum.march — Elixir-inspired lazy enumeration over any Iterable.

Enum.map(items, fn x -> x * 2)
Enum.filter(items, fn x -> x > 0)
Enum.fold(items, 0, fn acc x -> acc + x)
Enum.sort(items)
Enum.sort_by(items, fn x -> x.name)
Enum.chunk_every(items, 3)     -- group into chunks of 3
Enum.zip(items_a, items_b)
Enum.dedup(items)              -- remove consecutive duplicates
Enum.uniq(items)               -- remove all duplicates
Enum.take_while(items, pred)
Enum.drop_while(items, pred)
Enum.sum(nums)
Enum.product(nums)
Enum.scan(items, 0, fn acc x -> acc + x)
Enum.with_index(items)         -- [(0, x), (1, y), ...]
Enum.frequencies(items)        -- Map(a, Int) count of each item
Enum.min_by(items, fn x -> x.score)
Enum.max_by(items, fn x -> x.score)

Duration

duration.march — Time-span arithmetic.

let d = Duration.seconds(30)
let h = Duration.hours(2)
let w = Duration.weeks(1)

Duration.add(d, h)
Duration.subtract(h, d)
Duration.multiply(d, 3)
Duration.compare(d, h)     -- Int: negative, 0, positive
Duration.format(d)         -- "30s"
Duration.milliseconds(d)   -- 30000

URI

uri.march — URI parsing and construction.

URI.parse("https://example.com/path?k=v")
-- Result({ scheme, host, port, path, query, fragment })

URI.build({ scheme = "https", host = "example.com", path = "/api", ... })
URI.encode("hello world")     -- "hello%20world"
URI.decode("hello%20world")   -- "hello world"
URI.query_params("k=v&a=b")   -- [("k", "v"), ("a", "b")]

Module Quick Reference

Module Lines Purpose
Prelude Auto-imported helpers
List 508 Singly-linked list operations
String 364 String manipulation
Map HAMT-backed key-value map
Set HAMT-backed set
Option Maybe type helpers
Result Either type helpers
Enum 701 Lazy enumeration
Sort 615 Timsort, Introsort, specialized small sorts
Math 193 Arithmetic and transcendental functions
IO 72 Console I/O
System 155 OS/runtime info
File 139 File I/O
Dir 50 Directory operations
Path 91 Path manipulation
Crypto 302 SHA, HMAC, PBKDF2, random
UUID 266 UUID v4/v5
JSON JSON encode/decode
Base64 143 Base64 encode/decode
URI 360 URI parsing/construction
Duration 208 Time-span arithmetic
Logger Structured logging
Vault Process-local KV store
Http HTTP client
HttpServer HTTP/WebSocket server
Task Structured concurrency
Seq 251 Lazy sequences
IOList 221 Lazy string builder
Random Random number generation
Regex Regular expressions
BigInt Arbitrary-precision integers
Decimal Exact decimal arithmetic
CSV 100 CSV parsing
Datetime Date and time
Gen Property test generators (Hedgehog-style)
Check Property test runner with shrinking
Test Test assertion helpers

Next Steps

  • Property Testing — write property-based tests with Gen and Check
  • REPL — explore the stdlib interactively
  • Interfaces — how stdlib types implement interfaces
  • Toolingforge search to find functions by type or name