
[July 2026 edition]
Getting Started with Roc: A Pure Functional Language Meant to Ride Alongside Rust
Published: Jul 3, 2026
Reading time: ~6 min
Introduction
Hello, Asopi Tech here.
In the previous article — “Getting Started with Nim: A Multi-Paradigm Language That Transpiles to C” — I introduced Nim, one candidate AlopexDB is evaluating for its SQL parser rewrite. This time we look at the other star of the same investigation: Roc.
Roc is a relatively new pure functional language spearheaded by Richard Feldman, creator of Elm. Its distinctive design — “Rust host + Roc application” — is an attempt to bring functional programming into the systems world.
What kind of language is Roc?
Roc features:
- Pure functional — side effects are explicit; referential transparency holds
- Hindley-Milner type inference — strong static typing with virtually no annotations
- First-class ADTs + pattern matching — Tag Unions (
[Ok val, Err e]) sit at the center of the language - Perceus reference counting — a GC that aims for zero pause time and no scheduler tuning
- Compiles to native via LLVM — produces native binaries
- Platform / Host model — syscalls and side effects live in the “Platform”; the app is written as pure functions
The syntax feels very close to Elm or Haskell, and is unusually approachable at first read. Here’s a snippet from AlopexDB’s Roc SQL lexer trial (alopex-db/alopex experiment/sql-parser-trial):
Token : [Select, From, Where, Ident Str, IntLit I64, Star, Comma, Eq]
keyword : Str, Token -> Parser Utf8 Token
keyword = |text, tag|
const(tag) |> skip(string(text))
token : Parser Utf8 Token
token = one_of([
keyword("SELECT", Select),
keyword("FROM", From),
string("*") |> map(|_| Star),
string(",") |> map(|_| Comma),
many1(alpha) |> map(|chars| Ident(Str.from_utf8(chars))),
])If you’ve touched Elm or F#, the pipeline operator |> and the way tokens are pattern-matched into shape should feel natural.
Where is Roc as of mid-2026 — “not even 0.1 yet”
Roc is genuinely young. Its status in June 2026:
Version history
| Release | Date | Notes |
|---|---|---|
| alpha1 | 2025-01-29 | First alpha release |
| alpha2-rolling | 2025-01-29 | Massive syntactic overhaul |
| alpha3-rolling | 2025-02-26 | |
| alpha4-rolling | 2025-08-26 | Latest. Build speed improvements, new builtins |
The official position is explicit: “Roc is not ready for a 0.1 release yet.” No 1.0 timeline is published.
Breaking changes in alpha2 (January 2025)
alpha2 shipped sweeping breaking changes:
| Change | Before | After |
|---|---|---|
| Function calls | func arg | func(arg) |
| Naming convention | camelCase | snake_case |
| Error handling | Task type | ! suffix + ? operator |
| String interpolation | $(expr) | ${expr} |
| Logical operators | && / || | and / or |
| Tag construction | Ok val | Ok(val) |
A roc format --migrate migrator ships with the language, but the Task → ! rewrite is manual. The Alopex team reports 24 manual fixes were required for their SQL parser trial.
Expect more of the same as long as the language is in alpha.
The Zig compiler rewrite
The big infrastructure news is that the Roc compiler’s LLVM codegen has been rewritten from Rust (~18,000 lines) to Zig (~1,700 lines).
| Item | Detail |
|---|---|
| Size | 18,000 lines Rust → 1,700 lines Zig (10x shrink) |
| Architecture | Mono IR → Canonical IR (pre-monomorphization) |
| Not yet implemented | match expressions, lambdas, complex unions, refcounting, debug info |
| Top priority | Finish builtins (Issue #9596) |
Cutting an overweight Rust compiler and rebuilding it around Zig’s comptime is a bold call. Note that Roc hosts are still written in Rust — basic-webserver runs on Rust + hyper + tokio. Splitting “the language you write your compiler in” from “the language you write your app hosts in” is very much on-brand for Roc.
Community
- GitHub 5,700 stars / 387 forks / 43,653 commits
- Active Zulip chat
- Codebase is 92.9% Zig
What makes Roc interesting
Pure functional + ADT-first
AST definitions fall out naturally with Tag Unions, just like Elm:
SqlNode : [
Ident Str,
StrLit Str,
IntLit I64,
BoolLit Bool,
NullLit,
StarLit,
BinOp { op : BinaryOp, left : SqlNode, right : SqlNode },
UnOp { op : UnaryOp, operand : SqlNode },
FnCall { name : Str, args : List SqlNode },
ColRef { table : Str, column : Str },
SelectStmt {
columns : List SqlNode,
from : List SqlNode,
where : [Some SqlNode, None],
# ...
},
InsertStmt { table : Str, columns : List Str, values : List SqlNode },
# ...
]In Rust you’d reach for Box<SqlNode> and want to write #[allow(clippy::large_enum_variant)]. In Roc, Box never enters the user code — the runtime places things on the heap and manages them with Perceus refcounting.
Perceus reference counting
Roc’s GC strategy is called Perceus, borrowed from research on the Koka language. In short:
- Static analysis places refcount ops → minimizes atomic ops at runtime
- No stop-the-world
- No manual tuning
- Aims to unlock linear-type-based “in-place update” optimizations down the road
Platform / Host model
The most distinctive part of Roc is its Platform / Host model. All side effects — file IO, HTTP, TCP — live in the Platform. The app is a collection of pure functions.
Two major Platforms:
| Platform | Version | Overview |
|---|---|---|
| basic-cli | v0.20.0 (2025-08) | Files, HTTP, TCP, CLI args |
| basic-webserver | v0.13.1 (2026-01) | Web server backed by Rust (hyper + tokio) |
A Rust-written host loads the Roc app via C ABI:
Roc App (pure functions)
↓ exported as C ABI (roc_app_main, etc.)
Rust Host (side effects)
↓
OS / Network / DBThis aligns very neatly with AlopexDB’s “Rust storage engine + Roc parser” blueprint — the language boundary maps cleanly to a responsibility boundary.
Container development environment
Roc doesn’t ship an official Docker image yet, but curl + tar on a binary release gets you all the way there.
FROM debian:bookworm-slim AS roc-env
RUN apt-get update && apt-get install -y \
curl ca-certificates gcc libc6-dev \
&& rm -rf /var/lib/apt/lists/*
ARG ROC_VERSION=alpha4-rolling
RUN curl -OL https://github.com/roc-lang/roc/releases/download/${ROC_VERSION}/roc-linux_x86_64-${ROC_VERSION}.tar.gz \
&& tar xzf roc-linux_x86_64-${ROC_VERSION}.tar.gz \
&& mv roc-linux_x86_64-${ROC_VERSION} /opt/roc \
&& rm roc-linux_x86_64-${ROC_VERSION}.tar.gz
ENV PATH="/opt/roc:${PATH}"
WORKDIR /appFor reproducibility, Nix Flake works too:
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
roc.url = "github:roc-lang/roc";
};
outputs = { nixpkgs, roc, ... }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
rocPkgs = roc.packages.${system};
in {
devShells.${system}.default = pkgs.mkShell {
buildInputs = [ rocPkgs.cli ];
};
};
}Once you have the binary, container setup cost matches Nim. The only differentiator is whether an official image exists; inside a container everything reduces to “curl + tar.”
Runtime performance — data isn’t out yet
Honestly, Roc-vs-Rust benchmarks aren’t yet public. The official goal is “faster than mainstream GC languages (Go, C#, Java, JS),” but there’s no quantitative comparison with Rust or Nim at this time.
That said, LoC and build times are already very promising (Alopex trial, current implementation):
| Metric | Roc | Rust |
|---|---|---|
| Implementation LoC | 1,049 | 2,964 |
| release build | 4.7 s | 165 s |
| dev build | 0.8 s | — |
| Test runs (excl. build) | 0.3 s | 0.01 s |
| Projected SQLite-scale (dev) | ~5 s | — |
| Projected SQLite-scale (tests) | ~2 s | — |
release builds are slightly slower than Nim, but the dev-build feedback loop (0.8 s now → about 5 s at SQLite scale) is Roc’s real weapon.
Watch-outs when operating Roc
Real pain points hit during the AlopexDB trial:
--linker=legacyrequired on Linux — surgical linker issue #3609 is still openbasic-cliURL hash changes across versions — Dockerfile maintenance overhead- Unused imports are compile errors (exit code 2) — you must fix them
.rocfile naming rules are strict — CamelCase module names must match- API changes are frequent — see the 24 fixes above
The language design is refined, but production adoption is premature. The pragmatic stance is: run a PoC / experimental branch and evaluate ahead of a proper 1.0.
Who is Roc for, right now?
Roc is worth trying if you fit any of these:
- Elm or Haskell veteran who wants to bring functional programming closer to systems work
- You want to write AST definitions, parsers, or pure computation cores with GC-backed safety
- You’re curious about the “Rust host + functional app” architecture
- You want to follow language-implementation research (Perceus, Platform/Host, Canonical IR)
If you need to ship production services today or want a well-populated ecosystem, stay with Nim / Rust / Go for now.
Still — once the Zig compiler rewrite lands and the road from 0.1 to 1.0 is visible, Roc will offer a rare combination of “pure functional × native performance × Rust interop.” It’s very much worth watching now, before it goes mainstream.
Next up: an implementation-level comparison of both Nim and Roc SQL parsers side-by-side. See the next post — “Rust Is Not a Silver Bullet — Language Parsers Belong in Nim or Roc” — for the deep dive.
See you next time!
References
- Roc official site
- roc-lang/roc (GitHub)
- Roc Install Guide
- Roc Platforms and Apps — the official explanation of Platform/Host
- basic-webserver (Rust host example)
- roc-parser — Parser Combinator for Roc
- Roc with Richard Feldman — Rust in Production Podcast
- alopex-db/docs — AlopexDB research docs
- alopex-db/alopex
experiment/sql-parser-trial