Reference
Type system
The primitives, the two parameterised types, and the rules for conversion between them.
The primitive set, the two parameterised types, and what the language deliberately does not have. The generated syntax page lists the spellings; this one says what they mean.
Primitives
bool. The signed integers i8, i16, i32 and i64, and the unsigned u8, u16, u32 and u64. The floats f32 and f64. String. That is the whole set, and a bare number literal is f32 because the engines this language targets compute in single precision.
// Integers say what they do when a result does not fit. There is no undefined behaviour.
//
// A plain `+` fails rather than producing a value outside the type. `+%` wraps and `+|` saturates,
// and choosing is the point: a health bar saturates, a hash wraps, a count should fail.
fn health(current: u8, healed: u8) -> u8 {
return current +| healed
}
fn hashStep(accumulator: u32, value: u32) -> u32 {
return accumulator *% value
}
// Units are erased. `30m` is the number 30; `250ms` is 0.25, because seconds are the base unit.
fn reach() -> f32 {
return 30m
}
fn beat() -> f32 {
return 250ms
}
Literals take a width from context
A literal with an annotation takes that annotation's width, and one without takes f32. `let b: u8 = 200` is a u8 and 200 fits; a value that does not fit is refused at the literal rather than at the first arithmetic on it.
Two parameterised types, and no way to add a third
T? is an option and Result<T, E> is a result. You cannot declare your own generic type. Nothing in the language needs one, and the two that exist are about failure rather than about abstraction, which is a narrow enough job that a general mechanism would be a larger promise than the language wants to make.
// Failure is a value, and absence is a type.
//
// `load` cannot throw. Its caller either handles both cases or propagates with `?`, and the
// compiler is what makes that a choice rather than an oversight.
enum LoadError {
Missing
Corrupt
}
data Resource {
size: f32 = 0
}
fn load(present: bool) -> Result<Resource, LoadError> {
if present {
return Ok(Resource { size: 1024 })
} else {
return Err(LoadError.Missing)
}
}
// Propagate: on failure this returns the error unchanged, on success it continues with the value.
fn sizeOf(present: bool) -> Result<f32, LoadError> {
let resource = load(present)?
return Ok(resource.size)
}
// Or handle both cases explicitly. There is no third path where the error is ignored.
fn sizeOrZero(present: bool) -> f32 {
return match load(present) {
Ok(resource) => resource.size
Err(problem) => 0
}
}
Conversions are written, never inferred
There is no implicit widening and no implicit narrowing. Mixing widths in one expression names the conversions available and picks none of them, because the guess is where a value quietly changes width on a line that looks like arithmetic.
// Deliberately refused.
//
// There is no implicit widening. The compiler names the conversions rather than guessing which
// one was meant, because the guess is where a value quietly changes width.
fn add(a: u8, b: u32) -> u8 {
return a + b
}
What is not a type here
Vec3, Quat, Mat4, Transform and Color are not language types. They arrive through a linked capability, from whatever maths the host provides, so a consumer with no renderer still has a language and a host with different maths is not forced to agree with this one.
// The two prefixes, in one file.
//
// `std/math` is the language's own and is pure, so it links in any host. `drift/scene` is this
// engine's, and it carries the effect of reaching a scene. The prefix in the import is what tells
// you which of the two you are looking at.
//
// `Node` is not a language type either. It arrives with the capability, from whatever the host's
// scene is made of, which is why a consumer with no renderer still has a language.
import { clamp } from "std/math"
import { setRotation } from "drift/scene"
data Spin {
speed: f32 = 45deg
}
fn turn(spin: Spin, node: Node, dt: f32) {
let step = math.clamp(spin.speed * dt, 0, 90deg)
scene.setRotation(node, 0, 1, 0, step)
}