The Faby Documentation
The official specification and guide for Faby — the first programming language designed end-to-end by an AI model, Claude Fable-5. This is the Genesis release: the core design is specified; the reference implementation is in progress.
#Introduction
Faby is an AI-native, general-purpose programming language. It was conceived under a single guiding question: if most code in the future is generated, reviewed and maintained by machines, what should a language look like?
The answer differs from every mainstream language in four ways:
- Token economy. Grammar is optimized for minimal token cost during model generation — no semicolons, no redundant braces, significant newlines, and a literal syntax with zero ceremony.
- One canonical form. There is exactly one valid way to format any program.
faby fmtis the identity function on valid code, so every diff is a semantic diff. - Intent as syntax. The natural-language purpose of code travels inside the code via
intentblocks, where tooling can check it against actual behavior. - Verifiability first. Contracts (
ensure/expect) are part of the function signature, statically proven where decidable, guarded at runtime otherwise.
Faby is not a toy or a DSL. It targets the full stack: web services, autonomous agents, data pipelines, CLI tools, edge/WASM workloads and long-lived AI-maintained codebases.
Every feature must make code easier for a model to generate correctly and easier for a human to verify. If a feature serves only one of the two, it is rejected.
#Installation
The entire toolchain — compiler, interpreter, formatter, REPL, package manager and AI assistant — ships as one static binary with zero dependencies.
# macOS / Linux / Windows — via NPM (live now) $ npm i -g @fabycode/cli # verify $ faby version faby 0.1.0 (genesis) — designed by claude-fable-5
The Genesis runtime is live on NPM: npm i -g @fabycode/cli gives you faby run, faby repl and faby init today. The full native toolchain lands with v0.2 “Pulse”. See the complete CLI reference.
#Hello, Faby
A Faby program is a set of flows. Execution starts at flow main. The last expression of a flow is its return value — there is no return keyword for the common case.
flow main
print("Hello, Faby.")$ faby run hello.fy
Hello, Faby.Three rules you already learned from this example:
- Blocks are formed by indentation (two spaces) — the only legal indentation.
- Newlines terminate statements. There are no semicolons anywhere in the language.
- Comments start with
--and run to the end of the line.
#Flows & Values
Bindings
let creates an immutable binding; mut creates a mutable one. Immutability is the default because generated code is easier to verify when values don't change behind your back.
let answer = 42 -- Int, inferred
let pi = 3.14159 -- Float
let name = "Fable" -- Text
let ready = true -- Bool
let langs = ["faby", "fy"] -- [Text]
let scores = {alice: 9, bob: 7} -- {Text: Int}
mut counter = 0
counter += 1 -- only mut bindings may changeFlows
A flow declares its inputs and output type. Calls use named arguments when there is more than one parameter — models almost never scramble named arguments, and humans never have to count commas.
flow area(width: Float, height: Float) -> Float
width * height
flow main
let a = area(width: 3.0, height: 4.5)
print("area = {a}") -- string interpolation with {}String interpolation
Any expression can be embedded in a string with {…}. Formatting options follow a colon: {price:.2}.
There is one way to write everything above. No optional parentheses, no alternative block styles, no tabs-vs-spaces. faby fmt never changes a valid file.
#Semantic Types
Faby's type system encodes meaning, not just memory layout. Built-in semantic types like Email, Url, Time and Id validate at the boundary, so the inside of your program never handles malformed data.
Records
type User id: Id name: Text email: Email -- parse, don't validate joined: Time = Time.now() -- default value let u = User(id: Id.new(), name: "Ada", email: "ada@faby.dev") print(u.email.domain) -- semantic types carry behavior
Refinement types
Constrain any type with a where clause. The compiler proves refinements statically where it can and inserts a guard where it can't.
type Percent = Float where self >= 0.0 and self <= 100.0 type Port = Int where self > 0 and self < 65536 flow discount(price: Float, off: Percent) -> Float price * (1.0 - off / 100.0)
Unions & options
type Status = Active | Paused | Banned(reason: Text) -- Optionals are built in: T? is "T or nothing" flow find_user(id: Id) -> User? db.get(id)
#Pattern Matching
match is an expression and must be exhaustive — the compiler rejects a match that misses a case. Generated code cannot silently forget a branch.
flow describe(s: Status) -> Text
match s
Active -> "online"
Paused -> "away"
Banned(reason) -> "banned: {reason}"
flow classify(n: Int) -> Text
match n
0 -> "zero"
1 | 2 | 3 -> "small"
_ if n < 0 -> "negative"
_ -> "large"- Arms use
->; the matched value is destructured in place. |combines alternatives,ifadds a guard,_is the catch-all.
#Pipelines & Errors
The pipe operator
|> feeds the left value into the next flow. Data reads top-to-bottom, left-to-right — the way transformations are actually reasoned about. The shorthand .field inside a pipeline stage means “of the current element”.
let top_titles = articles |> filter(.score > 0.8) |> sort_by(.published, desc: true) |> take(10) |> map(.title)
Recovery operators
Any fallible expression is handled inline at the call site with ?. The failure policy is always visible exactly where failure can happen — no invisible exceptions, no forgotten error returns.
let page = web.fetch(url) ? retry(3) -- try again, then propagate
let rows = csv.parse(blob) ? skip -- drop failing elements
let cfg = load("cfg.fy") ? default(Cfg()) -- fall back to a value
let key = env("API_KEY") ? fail -- crash loudly, on purpose| Operator | Meaning |
|---|---|
| ? retry(n) | Re-attempt up to n times with exponential backoff, then propagate the error. |
| ? skip | In a collection context, drop the failing element; otherwise skip the statement. |
| ? default(v) | Substitute the value v on failure. |
| ? fail | Abort the current flow with a structured, traceable error. |
| ? handler(e) | Delegate to any flow that takes the error as input. |
#Concurrency
spawn introduces structured concurrency: every task started inside a spawn block must finish before the block's value is produced. Tasks cannot leak, and ownership rules make data races a compile-time error.
-- run one fetch per url, concurrently; failures drop out
flow crawl(urls: [Url]) -> [Page]
spawn for url in urls
web.fetch(url) ? skip
-- spawn an entire pipeline: stages run in parallel per element
let report = spawn readings
|> group_by(.sensor)
|> map_values(detect_anomalies)
-- two independent tasks, joined together
flow dashboard -> (Stats, [Alert])
let stats = spawn compute_stats()
let alerts = spawn scan_alerts()
(await stats, await alerts)If a Faby program compiles, it is free of data races. Mutable state never crosses a spawn boundary; channels and ownership transfer are the only ways to share.
#Contracts
Contracts make a flow's promises part of its signature. expect states what must be true on entry; ensure states what must be true about result on exit.
flow sqrt(x: Float) -> Float expect x >= 0.0 ensure result >= 0.0 ensure abs(result * result - x) < 1e-9 newton_raphson(x) flow transfer(from: Account, to: Account, amount: Money) expect amount > Money.zero expect from.balance >= amount ensure from.balance + to.balance == old(from.balance) + old(to.balance) ...
- Static first. The contract engine proves clauses at compile time where decidable.
- Guarded otherwise. Unproven clauses become runtime checks (removable per-build for hot paths — explicitly, never silently).
old(expr)refers to a value as it was on entry — essential for stating conservation laws.
#Intent Blocks
An intent block binds a natural-language statement of purpose to the flow that follows. It is not a comment: it is syntax, it is preserved through refactors, and tooling treats it as a checkable artifact.
intent "rank articles by relevance to the query, newest first on ties" flow rank(articles: [Article], query: Text) -> [Article] articles |> score_against(query) |> sort_by(.score, .published, desc: true)
faby check --intent asks a model to compare each intent against the code it governs and reports drift. In an AI-maintained codebase, intent blocks are how the “why” survives a thousand generated refactors.
#Standard Library
The standard library is small, audited and batteries-included for the AI era. Import modules with use.
| Module | Provides |
|---|---|
| core | Always imported: collections, Text, Time, math, print. |
| http | Declarative HTTP servers and a typed client. |
| web | Fetching, scraping, HTML/Markdown extraction. |
| db | Typed queries over SQLite/Postgres with semantic-type mapping. |
| ai | Typed model calls: complete, embed, classify, extract — cached and testable. |
| csv / json | Schema-driven parsing straight into your types. |
| stats | Descriptive statistics, distributions, anomaly helpers. |
| sys | Processes, env, files, args — capability-scoped. |
| test | Inline tests and property-based checks driven by contracts. |
The ai module
use ai -- extraction returns *your* type, validated like any other value type Invoice vendor: Text total: Float where self >= 0.0 due: Time flow parse_invoice(pdf_text: Text) -> Invoice ai.extract(from: pdf_text, model: "claude-fable-5") ? retry(2)
#CLI Reference
The CLI is published: npm i -g @fabycode/cli. This section is a summary — the complete CLI reference covers every command, flag, exit code, environment variable and CI recipe.
| Command | Description |
|---|---|
| faby run file.fy | Run a program with the interpreter (instant start). |
| faby build --native | Compile ahead-of-time to a single static binary. |
| faby build --wasm | Compile to WebAssembly for browser and edge targets. |
| faby check | Type-check, verify contracts, lint — no execution. |
| faby check --intent | Additionally verify intent blocks against code behavior. |
| faby fmt | Format to canonical form (identity on valid code). |
| faby test | Run inline tests and contract-derived property checks. |
| faby repl | Interactive session with full stdlib access. |
| faby explain file.fy | AI-generated plain-language explanation of any program. |
| faby add pkg | Add a dependency from the package index (lockfile, content-addressed). |
#FAQ
Is Faby only for AI-generated code?
No. Faby optimizes for the collaboration: models generate and refactor; humans read, steer and approve. Everything that makes Faby cheap for a model — one canonical form, visible failure policy, exhaustive matching — also makes it unusually pleasant to read.
Why a new language instead of a framework?
Token economy, canonical formatting, intent-as-syntax and contract verification cannot be retrofitted onto an existing grammar. They are properties of the language itself, or they don't exist.
Does Faby require an AI to compile or run?
Never. The compiler, runtime and all guarantees are fully deterministic and offline. AI is used only by opt-in tooling (faby explain, faby check --intent) and by the ai stdlib module if your program imports it.
Who designed Faby?
The language was conceived and specified by Claude Fable-5, an AI model by Anthropic, as the first programming language designed end-to-end by an AI — with humans as editors, critics, and first users.
How can I contribute?
v0.1 “Genesis” is an open design phase. Read this spec critically, file issues against the design, and propose changes. The decisions that survive contact with the community become v0.2.