Overview
Faber is a statically typed language for coding agents and human authors. It has a small mechanical grammar, explicit static and generic types, and math-oriented operators — and the same program can be written and read in eight language surfaces. This page is the claim plus the shape of everything; the pages below it go one level deeper.
One program#
This is the whole program the landing page opens with: a small type, a generic helper, and an entry point. Every panel on this page is compiler output, so the page cannot claim something the toolchain does not produce.
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
}Its real output:
$ faber run
127.5
outside
-4Reading it left to right, the shape repeats everywhere in the language:
the type comes before the name (f64 low), generics are written out
(fn choose<T>), ← binds a value at run time while = fixes a field's
shape at compile time, ÷ is true division and / floors (-7 / 2 is
-4), and main is the entry point. None of that changes when the keywords
are rendered in another language — the glyphs and the order stay put.
What the language gives you#
- A mechanical grammar. One construct has one spelling. Arrows mean
runtime effects (
←assign,→return,⇥error channel);=and:only state compile-time facts. Why the glyphs work this way. - Type-first declarations. A declaration is a type followed by a name —
f64 low, neverlow: f64— for parameters, locals and fields alike. Nullability isT ∪ none, and crossing between integer and float is an explicit↦. Types and values. - Math-oriented operators. Integer
/floors; true division is÷; comparisons read as math (≤,≥,≠,≈). When math and hardware convention disagree, Faber follows the math. - Widths apply at the store. Arithmetic runs unbounded; a width limit is applied once, where a value is stored into or converted to a bounded cell. Math in the ether.
- Reader locales, sealed. One reader locale per file, never mixed. The same program renders in eight surfaces and converts losslessly back to canonical Latin. Reader locales.
- Errors as values, tests as declarations. A fallible function declares
its error channel with
⇥; callers recover withdo/catch. Test suites live beside the code withdescribe,testandassert. Errors and testing.
Where it compiles#
Faber compiles through one analyzed program to many targets. Write a library,
emit it in the language your project already uses, and add it alongside your
existing code. This small class, with no generics and no main, is emitted
below exactly as radix emit --target rust produces it:
// Generated by radix - do not edit
// Requires the faber language-runtime crate (add to Cargo.toml):
// faber = { path = "../faber" } # adjust path for your layout
// SCR-09 recorded suppressions: identifier/literal fidelity, plan-directed
// casts and parameter passing, library-shaped artifact items.
#![allow(non_camel_case_types, non_snake_case, dead_code, clippy::unreadable_literal, clippy::cast_lossless, clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss, clippy::cast_precision_loss, clippy::needless_pass_by_value)]
#[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
}
}The table is read from faber targets, so it cannot drift from the toolchain.
Emits is source emission, Package is package assembly, Runs is running
through faber.
| Target | Emits | Package | Runs |
|---|---|---|---|
rust | yes | yes | yes |
faber | yes | — | — |
ts | yes | — | — |
go | yes | — | — |
python | yes | — | — |
haskell | yes | — | — |
wasm-text | yes | — | — |
wasm | yes | — | — |
llvm-text | yes | yes | yes |
metal-text | yes | yes | yes |
wgsl-text | yes | — | — |
sexp | yes | — | — |
swift | yes | — | — |
fhir | yes | yes | yes |
llvm-host | yes | yes | yes |
fmir-text | yes | yes | yes |
fmir | yes | yes | yes |
fmir-bin | yes | yes | yes |
Rust builds as a Cargo package today. Other targets give you source files to add to your existing 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 matrix records where. Read the target matrix.
Where to go next#
| If you want to | Read |
|---|---|
| See the reasoning behind the language | Behavior |
| Start writing code | Start |
| Look up a construct | Cheat sheet |
| Read the formal grammar | Grammar |
| Use the compiler | Faber command line |
| Pick another reader language | Reader locales |