DriftScript
Documentation

Rationale

Why units are erased

A wrapper would cost an object per value in the one path that budgets zero allocations.

Why are units erased instead of typed?

Because a units system that cannot be used in the frame loop is a units system for the parts of the program that did not need one, and a wrapper type is exactly that: it costs an object per value, in the one path that budgets zero allocations.

What erasure means here

30m generates the number 30. 250ms generates 0.25, because seconds are the base unit. 90deg generates radians at the literal. There is no wrapper, no tag, and nothing to unbox, so a unit is free at the only place it would have been expensive.

// 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
}

What it costs, said plainly

The dimension is not checked. Adding a duration to a distance compiles, because by the time the checker sees them they are both f32. What you get is the conversion, written at the literal where a reader can see it, and no more. A language claiming dimensional safety on this design would be claiming something it cannot do.