Skip to main content

literals

The forms that write a value out directly: the primitive literals, and the composite forms for a struct, a tuple, and an array.

const tracking_enabled: bit = true;
const home_station: string = "GroundStation-1";
const max_tracks: uint16 = 512;
const earth_radius_m: float64 = 6378137.0;

Primitive literals​

One form per primitive type. A numeric literal carries no width of its own — see numeric type inference for what pins it.

  • true and false are the bit literals.
  • A string literal is "-delimited, terminates on the line it opens, and recognizes the escapes \", \\, \n, \r, and \t.
  • An integer literal is decimal, 0x hexadecimal, or 0b binary, both prefixes lowercase.
  • A decimal literal needs an integer part, a ., and a fractional part, so 0.5 parses and .5 does not.
  • _ separates decimal digits. A literal may not open or close with one, hold two in a row, or place one before the exponent marker.
  • An e/E exponent is a positive integer. The hexadecimal and binary forms accept neither _ nor an exponent.
const label: string = "she said \"track\"";
const count: uint32 = 1_000_000;
const mask: uint8 = 0b10100000;
const signature: uint32 = 0x99aaBB;
const scale: float64 = 8.02838E16;

Ascription​

expr : Type pins an expression's type. It constrains inference and performs no conversion.

  • The ascribed type becomes the expression's type, and an inner type it does not accept is a mismatch.
  • Ascription binds below every operator, so an un-grouped ascription reaches as far left as it can: 1 + 2: int32 ascribes the sum.
  • Parentheses are what ascribe one operand: (1: int32) + 2.
(42: int16)         // => 42
(1 + 2: int32)      // => 3
(0.5: float32)      // => 0.5

Numeric type inference​

A numeric literal stands for a width still to be decided, and unification with the constraints around it decides one. Other forms open the same hole: an empty [] and a context-less none.

  • Constraints come from an ascription, a typed binding, and a declared type — a const's annotation, a return type, a parameter type, a struct field type — as well as from the operators applied.
  • Enforcement recurses through structure, so a declared int32[] return type pins the literals in a comprehension over [1, 2, 3].
  • Inference is solved one declaration at a time. No constraint crosses a declaration boundary, so a use of a declaration reads its declared type and never its initializer's holes.
  • A width still unconstrained once its declaration is solved is reported. No width is guessed.
function scaled(factor: int32) -> int32[] = map(x in [1, 2, 3]) { x * factor; };

Struct literals​

TypeRef { field = expr; … } — one binding per field, each closed by a semicolon. The literal's type is the referenced declaration.

  • Every field gets exactly one binding, fields inherited through extends included.
  • Bindings may appear in any order, and each must be assignable to the field's declared type.
  • A zero-field struct is written Empty{}.
  • TypeRef { base with field = expr; } copies base and overrides the fields it names, so an update binds only what it changes.
struct TrackReport {
    track_id: uint32;
    bearing_deg: float32;
}

const initial: TrackReport = TrackReport { track_id = 1; bearing_deg = 87.5; };
const turned: TrackReport = TrackReport { initial with bearing_deg = 91.0; };

Tuple literals​

A parenthesized list of two or more expressions, typed by its elements in order.

  • A single parenthesized expression is a grouping, not a one-element tuple.
  • Each element's type is determined independently.
  • An element may carry its own ascription, or the whole tuple may be ascribed.
const fix: (float64, float64) = (42.3601, -71.0589);

(1: int32, 2: int32)      // => (1, 2)
((1, 2): (int32, int32))  // => (1, 2)
fix._1                    // => -71.0589

Array literals​

A bracketed list of expressions of one type. A for after the first element makes it a comprehension instead of a list.

  • All elements share one type.
  • An empty [] takes its element type from context.
  • ++ concatenates two arrays of one element type.
const bearings: float32[] = [87.5, 91.0];

bearings ++ [(274.25: float32)]    // => [87.5, 91, 274.25]
length(bearings)                   // => 2

Errors​

A numeric literal nothing pins​

The width has to come from somewhere. An unconstrained literal is reported rather than defaulted.

function total(count: uint32) -> uint32 = {
    let scaled = 2 * 8;   // error: ambiguous-inferred-type
    count;
};

A field the struct does not declare​

struct TrackReport {
    track_id: uint32;
}

const initial: TrackReport = TrackReport { track_id = 1; bearing_deg = 87.5; };   // error: invalid-member-access

A field bound twice​

struct TrackReport {
    track_id: uint32;
    bearing_deg: float32;
}

const initial: TrackReport = TrackReport { track_id = 1; track_id = 2; bearing_deg = 87.5; };   // error: duplicate-field-binding

A field left unbound​

A literal states the whole value, so every field needs a binding. An update is the form that changes part of one.

struct TrackReport {
    track_id: uint32;
    bearing_deg: float32;
}

const initial: TrackReport = TrackReport { track_id = 1; };   // error: missing-field
  • operators — what may be applied to a literal, and at what precedence.
  • struct — the declaration a struct literal names.
  • array — indexing, slicing, and the array type itself.