Docs / v0.1 “Genesis”

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.

SPEC v0.1FILE EXT .fyLICENSE MITSTATUS: DESIGN FREEZE CANDIDATE

#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 fmt is 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 intent blocks, 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.

Design Principle 0

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.

terminal
# 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
Status

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.

hello.fy
flow main
  print("Hello, Faby.")
terminal
$ 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.

bindings.fy
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 change

Flows

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.

flows.fy
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}.

Canonical Form

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

types.fy
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.

refinements.fy
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

unions.fy
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.

match.fy
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, if adds 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”.

pipes.fy
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.

errors.fy
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
OperatorMeaning
? retry(n)Re-attempt up to n times with exponential backoff, then propagate the error.
? skipIn a collection context, drop the failing element; otherwise skip the statement.
? default(v)Substitute the value v on failure.
? failAbort 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.

spawn.fy
-- 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)
Guarantee

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.

contracts.fy
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.fy
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.

ModuleProvides
coreAlways imported: collections, Text, Time, math, print.
httpDeclarative HTTP servers and a typed client.
webFetching, scraping, HTML/Markdown extraction.
dbTyped queries over SQLite/Postgres with semantic-type mapping.
aiTyped model calls: complete, embed, classify, extract — cached and testable.
csv / jsonSchema-driven parsing straight into your types.
statsDescriptive statistics, distributions, anomaly helpers.
sysProcesses, env, files, args — capability-scoped.
testInline tests and property-based checks driven by contracts.

The ai module

ai.fy
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

Live on NPM

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.

CommandDescription
faby run file.fyRun a program with the interpreter (instant start).
faby build --nativeCompile ahead-of-time to a single static binary.
faby build --wasmCompile to WebAssembly for browser and edge targets.
faby checkType-check, verify contracts, lint — no execution.
faby check --intentAdditionally verify intent blocks against code behavior.
faby fmtFormat to canonical form (identity on valid code).
faby testRun inline tests and contract-derived property checks.
faby replInteractive session with full stdlib access.
faby explain file.fyAI-generated plain-language explanation of any program.
faby add pkgAdd 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.