Reference
Syntax cheat sheet
Every form of the language in one place: declarations, expressions, patterns, types and the call rules, with the exact spelling of each.
Every form the language has, with its exact spelling. Each line is the canonical form: it is what torb format writes and
what the compiler accepts.
Example#
use File from "std/fs"
type Config {
var host: String = "localhost"
var port: Int = 8080
}
type ConfigError {
case Missing(path: String)
case Malformed(line: Int)
}
fn load(path: String): Result<Config, ConfigError> {
if !File.exists(path) {
return Fail ConfigError.Missing(path)
}
Config()
}
match load("project.trb") {
Ok(config) => print "{config.host}:{config.port}"
Fail(problem) => printError "{problem}"
}Syntax#
Declarations#
const name = value an immutable binding
var name: Type = value a mutable binding with an annotation
fn name(a: Int, b: Int = 1): Int { ... } a function; a default is evaluated at every call
fn name(): Int { ... } a method, inside a type body; it does not list `self`
var fn name() { ... } a method that changes its receiver in place
static fn name(): Self { ... } a member of the type itself, reached as `Type.name()`
static name = value a constant of the type
fn name(var target: Counter) { ... } a parameter the function may change
fn name(...rest: Int): Int { ... } a variadic parameter; `rest` is a `List<Int>`
type Name { ... } a type
type Name = Other an alias for an existing type
case Name = 1 inside a type body: a case that stands for a number, `rawValue()`
type Name with Trait by field { ... } the trait's required members are forwarded to field
type Name with (Trait, Trait) by field { ... } several traits delegated to the same field
shared type Name { ... } a type with an identity instead of a value
trait Name { ... } a capability
extend Name with Trait { ... } an implementation written afterwards
extend Name { ... } members added to a type
public fn name() { ... } exported from its file
private var field: Int = 0 invisible outside its type
protected var field: Int = 0 write-protected: read by everyone, written by its own file only
deprecated("why", replacement: "other") on the line above a declaration: kept, warned about, rewritten by lint
use Name from "std/core" an import
use Type.Case from "./module" a case, by its path
use Type.member from "acme/text" a member another package's `extend` adds
use * as console from "std/console" a namespace import
use Name as Other from "./module" an import under a local name
public use Name from "./module" a re-exportExpressions#
f a, b a command call: the canonical form
f(a, b) needed when a rule of the canon says so
f() no arguments: always parentheses
f(a) { x => x } a trailing closure
f a { x => x } a command call with a trailing closure
f(label: value) a labelled argument; labels follow the positional ones
f(...values) a spread of any `Iterate`
{ x: Int => x * 2 } a closure with a typed parameter
{ var point => point.x = 0 } a closure that binds a `var` parameter, the caller's value
{ _ * 2 } a closure with implicit parameters `_`, `_2`, `_3`
Type.Case(field) a case, written out
.Case(field) a case where the expected type is known
Case(field) a case that the file imports
value.member a method reference, bound to `value`
Type.member the member itself: `(self: Type) => ...`
value? unwrap an `Ok` or a `Some`, or return early
value ?? fallback `OrElse.orElse`: the value, or a lazy fallback
a ** b `Power.power`: tighter than `*`, to the right; `-x ** 2` is an error
a & b a | b a ^ b ~a the bit operators of `Bits`: `&` binds like `*`, `|` and `^` like `+`
a << n a >> n shifts, between `*` and `**`; arithmetic on a signed type
value?.member `Option.map`, or `flatMap` for an optional member
value.into() a conversion chosen by the expected type
value.to<Target>() a conversion with the target written out
do { ... } a block evaluated immediately
if condition { a } else { b } an expression
match subject { pattern => value } an expression, and exhaustive
"text {expression}" interpolation; a literal brace is `\{`
raw"text" a raw string: no escapes, no interpolation
const page: Uri = "https://example.test/" a literal the compiler reads: `Path`, `Uri`, `Regex`, ...
const digits: Regex = "\d{2,4}" a `Regex` or `UriTemplate` literal is read verbatim
""" a multi-line string, dedented by its first line
text
"""Patterns#
_ the wildcard
name binds, because it starts lowercase; a refutable arm has to read it
_name binds and keeps the name; nothing has to read it
Case(field) an imported case; never binds
.Case(field) a case of the type being matched
Type.Case(field) a case written out
Type(field, other) a type read backwards, field by field
Type(label: value) a labeled sub-pattern; the labeled ones follow the positional ones
Type(field, ...) `...` stands for every field the pattern does not name
(a, b) a tuple
[first, ...rest] a list, with a rest
[] the empty list
1 | 2 | 3 alternatives
4..=9 a range
n if n < 0 a binding with a guardTypes#
Int Int8 Int16 Int32 Int64 signed; `Int` is an alias for `Int64`
UInt UInt8 UInt16 UInt32 UInt64 unsigned
Float Float32 Float64 `Float` is an alias for `Float64`
Bool Char String Void Never the rest of the built-ins
Value? `Option<Value>`
List<Item> Map<Key, Value> Set<Item> collection traits
Array<Item, 16> a fixed size in the type
Range<Int> `0..10`, `0..=10`, `0..`, `..10`
(Int, String) a tuple
(lowest: Int, highest: Int) a labelled tuple; a label is not part of the type
(value: Int) => Int a function type
(var self: Config) => Void a receiver closure
lazy Value evaluated at most once, on first use
Expression<Bool> the value and its syntax tree
Show & Encode an intersection of traits
"tcp" | "udp" a union of literals of one base typeStatements#
return value an early return
for item in items { ... } over anything `Iterate`
for (key, value) in table { ... } destructuring in a loop
for var item in items { ... } every slot in place: `item` is `items[key]` of the turn
for (key, var value) in table { ... } every value of a map in place, the key a copy
while condition { ... } `break` and `continue` work
loop { ... } endless: `Never` without a `break`, `Void` with one
if const Some(user) = find(id) { ... } a pattern in a condition
if var Some(cursor) = current { ... } binds into the place, not into a copy
place = value a statement, never an expression
panic "message" aborts with exit code 101Rules#
- A statement ends at the end of its line. There are no semicolons, and two statements never share a line.
- A call is a command wherever the grammar allows it: in command position, with a path as the callee, at least one
argument whose first token is not
(,[,-,!or., no operator at the top level of an argument, and all arguments on one line. Parentheses everywhere else. - A case is never bare unless the file imports it.
Some,None,OkandFailare bare because the prelude imports them. - In a pattern, a lowercase name binds and an uppercase name never does. An uppercase binding is a compile error.
- A binding of a refutable pattern has to be read. In an arm of a
match, anif const/if varand awhile const, a binding the guard and the body never read is an error: write_, or_nameto keep the name. - A name is ASCII, and its first letter is a rule.
[A-Za-z_][A-Za-z0-9_]*;A-Zstarts a type, a trait, a case, a type parameter and a type alias, and everything else starts lowercase. There is noMAX_SIZE. constis deep. Through aconstbinding nothing changes.- A field is
constunless markedvar; a member is public unless markedprivate; a top-level declaration is private to its file unless markedpublic. - A
publicfunction and a trait method never infer their result type. Without one they answerVoid. {in expression position is always a closure, never a block.do { ... }evaluates a block immediately.- A
matchis exhaustive, and an unreachable arm is an error. - An expression statement has to be
VoidorNever, unless the call has avarreceiver or avarargument. - Mutation needs a
varpath from the binding down: avarbinding,varparameter or avar fnreceiver, thenvarfields. - A
varthat is changed and never read afterwards is a compile error, and so is the discarded result of a method that reads its receiver. - An endless loop is
loop.loop { ... }has the typeNeverwithout abreakthat targets it andVoidwith one, abreakcarries no value, andwhile trueis an error that namesloop. - A
usenames what it imports, and a member of a foreignextendis one of those names.use String.shout from "acme/text"is the same form as a case import; what the type's own package attaches to it needs no import, and what a trait puts on a foreign type needs the trait.
What this is not#
This page is every form that is right. The forms that look right and are not - let, a semicolon, a bare case,
Err, text.length, a & b, x as Int, MAX_SIZE, while true, impl Trait for Type and the rest - are one list,
each with the diagnostic it produces:
What a model trained on other languages gets wrong.
Related#
- Command calls - the call rules in full, with the canon.
- Bindings -
const,varand the copy trap. - Cases and match - how a case is spelled and matched.
- Result -
Ok,Failand?. - Traits -
with,extendand the trait names. - use - every form of an import, the path of a member included.
- Loops -
for,whileandloop, and what each one produces. - What a model trained on other languages gets wrong - the forms that look right and are not, with their diagnostics.