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 OR Apache-2.0

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 renders in eight reader pack 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.15.1

Give your model this link

faberlang.dev/install.md
Version
1.15.1
Platforms
macOS arm64 · Linux x64
License
MIT OR Apache-2.0

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 compiler front end, runner, standard library, and grammar are open source under MIT OR Apache-2.0. The official release also carries code generators that are not in the public source. Faber is experimental through version 1.

main.fab · reader locale
faber convert --to en — English reader surface — the base spelling for everyday source
class Span {
    let f64 low
    let 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 {
    let 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 — Latin — the classical surface the language is named for
genus Span {
    sit f64 low
    sit 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 {
    sit 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 {
    đặt f64 low
    đặt 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 {
    đặt 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. English is the maintained documentation tree; each reader locale has a mapping page, and 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 pack 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

    284 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 {
    let f64 low
    let 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, p_x: f64) -> bool {
        self.low <= p_x && p_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
// … 5 lines of generated header and runtime shim elided …

class Span {
    low!: number;
    high!: number;
    toString(): string {
        return "Span { low = " + display.value(this.low, "fractus", { true_: "true", false_: "false", none: "none" }) + ", high = " + display.value(this.high, "fractus", { true_: "true", false_: "false", none: "none" }) + " }";
    }
    contains(p_x: number): boolean {
        return (this.low <= p_x) && (p_x <= this.high);
    }
    center(): number {
        return ((this.low + this.high) / 2);
    }
}
radix emit --target go span.fab — a source file in package main
// Code generated by faber. DO NOT EDIT.

package main

type Span struct {
    Low float64
    High float64
}

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

func (self *Span) Center() float64 {
    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

func faberFractusPlain(_ description: String) -> String {
    var text = Substring(description)
    var sign = ""
    if text.hasPrefix("-") {
        sign = "-"
        text = text.dropFirst()
    }
    var exponent = 0
    if let marker = text.firstIndex(where: { $0 == "e" || $0 == "E" }) {
        exponent = Int(text[text.index(after: marker)...]) ?? 0
        text = text[..<marker]
    }
    var whole = Substring(text)
    var fraction = Substring("")
    if let dot = text.firstIndex(of: ".") {
        whole = text[..<dot]
        fraction = text[text.index(after: dot)...]
    }
    let digits = Array(String(whole) + String(fraction))
    let point = whole.count + exponent
    var shown: String
    if point <= 0 {
        shown = "0." + String(repeating: "0", count: -point) + String(digits)
    } else if point >= digits.count {
        shown = String(digits) + String(repeating: "0", count: point - digits.count) + ".0"
    } else {
        shown = String(digits[..<point]) + "." + String(digits[point...])
    }
    return sign + shown
}

func faberFractus64(_ value: Double) -> String {
    if value.isNaN { return "NaN" }
    if value.isInfinite { return value < 0 ? "-inf" : "inf" }
    if value.rounded(.towardZero) == value {
        let sign = value.sign == .minus ? "-" : ""
        let magnitude = value.magnitude
        if magnitude < 9.0e18 { return sign + String(UInt64(magnitude)) + ".0" }
        var digits = Array(String(magnitude.significandBitPattern | (UInt64(1) << 52)).utf8.reversed().map { $0 - 48 })
        for _ in 0..<(Int(magnitude.exponent) - 52) {
            var carry: UInt8 = 0
            for index in 0..<digits.count {
                let doubled = digits[index] * 2 + carry
                digits[index] = doubled % 10
                carry = doubled / 10
            }
            if carry > 0 { digits.append(carry) }
        }
        return sign + String(decoding: digits.reversed().map { $0 + 48 }, as: UTF8.self) + ".0"
    }
    return faberFractusPlain(value.description)
}

func faberFractus32(_ value: Float) -> String {
    if value.isNaN { return "NaN" }
    if value.isInfinite { return value < 0 ? "-inf" : "inf" }
    return faberFractusPlain(value.description)
}

class Span {
    var low: Double
    var high: Double

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

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

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

extension Span: CustomStringConvertible {
    var description: String { return "Span { low = \(faberFractus64(self.low)), high = \(faberFractus64(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 284 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 @ kernel 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. It is under active development; 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.