Faber

Experimental through version 1. Version 0 was alpha. This is the first language release: the interface is relatively stable, not frozen. Nothing is promised stable until version 2.

A statically typed language for coding agents and human authors · MIT

Written by models,
read in your language.

Faber Romanus has a clear mechanical grammar, explicit static and generic types, and math-oriented operators. The same program is written and read in eight language surfaces, and compiles to Rust, TypeScript, Go, Swift and more — so a library you write once can go into the project you already have.

Faber · agent passv1.12.0

Give your model this link

faberlang.dev/install.md
Version
1.12.0
Platforms
macOS arm64 · Linux x64
License
MIT

Your model reads that file, downloads the current release for your machine, verifies its checksum, installs the faber command, and runs faber check on a hello program. It all happens on your machine.

The language, public libraries, examples, and tooling ship under the MIT license. Radix, the compiler, is closed only while it is under active development. That is temporary, not a permanent fence.

main.fab · reader locale
faber convert --to en — English reader surface — the base spelling for everyday source
class Span {
    const f64 low
    const f64 high

    fn contains(f64 x) → bool {
        return self.low ≤ x and x ≤ self.high
    }

    fn center() → f64 {
        return (self.low + self.high) ÷ 2.0
    }
}

fn choose<T>(bool first, T a, T b) → T {
    return first ✓ a ✗ b
}

main {
    const Span bytes ← Span { low = 0.0, high = 255.0 }
    print bytes.center()
    print choose(bytes.contains(300.0), "inside", "outside")
    print -7 / 2
}
faber convert --to la — canonical Faber — the classical surface the language is named for
genus Span {
    fixum f64 low
    fixum f64 high

    functio contains(f64 x) → bivalens {
        redde ego.low ≤ x et x ≤ ego.high
    }

    functio center() → f64 {
        redde (ego.low + ego.high) ÷ 2.0
    }
}

functio choose<T>(bivalens first, T a, T b) → T {
    redde first ✓ a ✗ b
}

incipit {
    fixum Span bytes ← Span { low = 0.0, high = 255.0 }
    nota bytes.center()
    nota choose(bytes.contains(300.0), "inside", "outside")
    nota -7 / 2
}
faber convert --to th-TH — Thai — spaceless script
ชนิด Span {
    คงที่ f64 low
    คงที่ f64 high

    ฟังก์ชัน contains(f64 x) → ตรรกะ {
        คืน ตัวฉัน.low ≤ x และ x ≤ ตัวฉัน.high
    }

    ฟังก์ชัน center() → f64 {
        คืน (ตัวฉัน.low + ตัวฉัน.high) ÷ 2.0
    }
}

ฟังก์ชัน choose<T>(ตรรกะ first, T a, T b) → T {
    คืน first ✓ a ✗ b
}

เริ่ม {
    คงที่ Span bytes ← Span { low = 0.0, high = 255.0 }
    บันทึก bytes.center()
    บันทึก choose(bytes.contains(300.0), "inside", "outside")
    บันทึก -7 / 2
}
faber convert --to zh-Hans — Simplified Chinese
类 Span {
    常量 f64 low
    常量 f64 high

    函数 contains(f64 x) → 布尔 {
        返回 自身.low ≤ x 且 x ≤ 自身.high
    }

    函数 center() → f64 {
        返回 (自身.low + 自身.high) ÷ 2.0
    }
}

函数 choose<T>(布尔 first, T a, T b) → T {
    返回 first ✓ a ✗ b
}

入口 {
    常量 Span bytes ← Span { low = 0.0, high = 255.0 }
    显示 bytes.center()
    显示 choose(bytes.contains(300.0), "inside", "outside")
    显示 -7 / 2
}
faber convert --to zh-Hant — Traditional Chinese
類型 Span {
    定值 f64 low
    定值 f64 high

    函式 contains(f64 x) → 布林 {
        傳回 自身.low ≤ x 且 x ≤ 自身.high
    }

    函式 center() → f64 {
        傳回 (自身.low + 自身.high) ÷ 2.0
    }
}

函式 choose<T>(布林 first, T a, T b) → T {
    傳回 first ✓ a ✗ b
}

入口 {
    定值 Span bytes ← Span { low = 0.0, high = 255.0 }
    註記 bytes.center()
    註記 choose(bytes.contains(300.0), "inside", "outside")
    註記 -7 / 2
}
faber convert --to vi — Vietnamese
kiểu Span {
    hằng f64 low
    hằng f64 high

    hàm contains(f64 x) → logic {
        trả tôi.low ≤ x và x ≤ tôi.high
    }

    hàm center() → f64 {
        trả (tôi.low + tôi.high) ÷ 2.0
    }
}

hàm choose<T>(logic first, T a, T b) → T {
    trả first ✓ a ✗ b
}

bắt_đầu {
    hằng Span bytes ← Span { low = 0.0, high = 255.0 }
    ghi_chú bytes.center()
    ghi_chú choose(bytes.contains(300.0), "inside", "outside")
    ghi_chú -7 / 2
}
faber convert --to ar — Arabic — right-to-left, bidi isolated
صنف Span {
    ثابت f64 low
    ثابت f64 high

    دالة contains(f64 x) → منطقي {
        أعد ذات.low ≤ x و x ≤ ذات.high
    }

    دالة center() → f64 {
        أعد (ذات.low + ذات.high) ÷ 2.0
    }
}

دالة choose<T>(منطقي first, T a, T b) → T {
    أعد first ✓ a ✗ b
}

بداية {
    ثابت Span bytes ← Span { low = 0.0, high = 255.0 }
    اعرض bytes.center()
    اعرض choose(bytes.contains(300.0), "inside", "outside")
    اعرض -7 / 2
}
faber convert --to hi — Hindi — Devanagari
वर्ग Span {
    स्थिर f64 low
    स्थिर f64 high

    फलन contains(f64 x) → तार्किक {
        लौटाओ मैं.low ≤ x और x ≤ मैं.high
    }

    फलन center() → f64 {
        लौटाओ (मैं.low + मैं.high) ÷ 2.0
    }
}

फलन choose<T>(तार्किक first, T a, T b) → T {
    लौटाओ first ✓ a ✗ b
}

आरंभ {
    स्थिर Span bytes ← Span { low = 0.0, high = 255.0 }
    दिखाओ bytes.center()
    दिखाओ choose(bytes.contains(300.0), "inside", "outside")
    दिखाओ -7 / 2
}
Output
$ faber run
127.5
outside
-4

One program, eight readings. Every tab is the compiler’s own rendering (faber convert), and each one runs. Keywords, types and diagnostics change; identifiers and string literals do not.

Reader locales

Start in your own language

People should not need English to use a model for code, or to read what the model wrote. Each language has its own documentation home, written in that language. Source is written the same way: one reader locale per file, sealed against the others, so a Thai file contains Thai keywords and nothing else.

Latin is the canonical interchange form the language is named for. Reader locales also localize compiler diagnostics. See all languages · How reader locales work

Why Faber

Built for models to write

A model writes best against a surface with few surprises. Faber keeps the rules small, regular and explicit, then shows the result in the reader’s language.

  1. A mechanical grammar

    258 grammar productions, generated and checked as the parser’s authority. One construct has one spelling. Arrows mean runtime effects (← assign, → return, ⇥ error channel); = and : only state compile-time facts. Read the grammar

  2. Explicit static and generic types

    Declarations are type-first: f64 low, never low: f64. Nullability is written T ∪ none. Generics are written out (fn choose<T>), and crossing between integer and float is an explicit ↦, not an accident. Types and values

  3. Math-oriented operators

    Integer / floors, so -7 / 2 is -4; true division is ÷. Comparisons read as math (≤ ≥ ≠ ≈), and tensor work has its own operators (· matmul, ⊙ elementwise). When math and hardware convention disagree, Faber follows the math. Glyphs and Latin

  4. Machine-readable by design

    Diagnostics are coded and explainable. The documentation ships an agent index and focused skill guides at fixed paths, so a model can learn the language from the site itself. /llms.txt

Cross-compile

Write it once. Put it in the project you already have.

Faber compiles through one analyzed program to many targets. Write a library in Faber, emit it in the language your project already uses, and add it alongside your existing code. This small library, with no generics and no main, is emitted below exactly as radix emit produces it, and each panel was checked in its own toolchain.

span.fab · source
@ public
class Span {
    const f64 low
    const f64 high

    fn contains(f64 x) → bool {
        return self.low ≤ x and x ≤ self.high
    }

    fn center() → f64 {
        return (self.low + self.high) ÷ 2.0
    }
}
span.fab → target
radix emit --target rust span.fab — builds as a Cargo package with faber build; uses the small faber runtime crate
// … 9 lines of generated header and runtime shim elided …

#[derive(Clone, PartialEq)]
pub struct Span {
    pub low: f64,
    pub high: f64,
}

impl ::core::fmt::Debug for Span {
    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
        write!(f, "Span {{ low = {}, high = {} }}", faber::display_fractus(*&self.low), faber::display_fractus(*&self.high))
    }
}

impl Span {
    pub fn contains(&self, x: f64) -> bool {
        self.low <= x && x <= self.high
    }
    pub fn center(&self) -> f64 {
        (self.low + self.high) / 2.0
    }
}
radix emit --target ts span.fab — a source file you add to your project; imports @faber/runtime
// … 14 lines of generated header and runtime shim elided …

class Span {
    low!: number;
    high!: number;
    toString(): string {
        return "Span { low = " + __faberDisplay(this.low, "fractus") + ", high = " + __faberDisplay(this.high, "fractus") + " }";
    }
    contains(x: number): boolean {
        return ((this.low <= x) && (x <= this.high));
    }
    center(): number {
        return ((this.low + this.high) / 2);
    }
}
radix emit --target go span.fab — a source file in package main
// Generated by radix - do not edit

package main

type Span struct {
    Low float64
    High float64
}

func (self *Span) Contains(x float64) bool {
    _ = self
    return ((float64(self.Low) <= float64(x)) && (float64(x) <= float64(self.High)))
}

func (self *Span) Center() float64 {
    _ = self
    return float64(((self.Low + self.High) / 2.0))
}
radix emit --target swift span.fab — a source file; no runtime dependency
// Generated by radix - do not edit

class Span {
    var low: Double
    var high: Double

    init(low: Double, high: Double) {
        self.low = low
        self.high = high
    }

    func contains(x: Double) -> Bool {
        return ((self.low <= x) && (x <= self.high))
    }

    func center() -> Double {
        return ((self.low + self.high) / 2.0)
    }
}

extension Span: CustomStringConvertible {
    var description: String { return "Span { low = \(String(describing: self.low)), high = \(String(describing: self.high)) }" }
}

Rust builds as a Cargo package today. TypeScript, Go and Swift give you source files to add to your project; assembling them into installable packages is not built yet. Support is stated target by target: generics and some operators do not lower to every target, and the target matrix records where. Read the target matrix

What each target does today. The first three columns are read from faber targets.
TargetEmitsPackageRunsIn practice
Source for your existing project
RustyesyesyesThe primary target: a full Cargo package, and runnable through faber.
TypeScriptyes——Source emission; package assembly is not built yet.
Goyes——Source emission; package assembly is not built yet.
Swiftyes——Source emission; a subset.
Pythonyes——Source emission; a limited subset.
Haskellyes——Source emission; a limited subset.
Systems, native and device
Native executableyesyesyesMIR lowered to LLVM, linked for the local host.
WebAssemblyyes——WAT text; binary conversion uses external tools.
LLVM IRyesyesyesText for LLVM tooling; also the CUDA device route.
MetalyesyesyesShader source; device execution through the Metal route.
WGSLyes——WebGPU compute shader source.
Portable package images
FHIRyesyesyesA portable analyzed-program package envelope.
FMIRyesyesyesA source-independent package image that faber can run.

Libraries

Libraries written in Faber

The libraries are ordinary Faber source. They show what the language is for, and each one can be read, changed and emitted like your own code. Status is stated plainly.

A low-poly 3D scene of a bridge with towers and lamp posts over water, rendered by Triga
Scene graph, materials, lighting — triga-budapest
A procedurally generated 3D terrain with lakes and hills, rendered by Triga
Procedural heightmap terrain, biome shading
Eight primitive 3D shapes — cylinder, cone, cube, torus, plane and others — rendered by Triga
Primitive geometry set from triga:geometria

Example scenes built with Triga.

Grammar

A grammar you can read in one sitting

The language is specified as 258 productions in a single EBNF, generated from one source and published in full. It is large enough to be expressive and regular enough to hold in your head or a model’s context. The reference pages are generated from the same source, so they cannot disagree with the parser.

Compute

Compute is one thing you can build

The same language carries numeric and device work. Tensor operators and @ nucleum kernels lower to WGSL, Metal and CUDA routes, and Gradus uses them for autograd. Device execution is explicit and fail-closed: a requested backend never silently falls back to CPU. Bounded training runs on accepted Metal and CUDA machines; device inference is not shipped.

Doors

Where to go

For models

Reading this as a model?

Machine surfaces are locale-less and live at the root: /install.md to install, /llms.txt for the index, /agents/index.md for the learning path, and /.well-known/agent-skills/ for focused skill guides.