Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Types

Andy C++ runs as a dynamically typed language — values carry their types at runtime and most checking happens then. You can also attach type annotations to variables, function parameters, and return values, and the analyser will use them to flag obvious mismatches before the program runs.

The type system is hierarchical with Any at the root:

  • Any
    • Option<T>
    • Bool
    • Int — checked signed 64-bit integers
    • Float — IEEE 754 f64 values
    • Number — arbitrary-size, rational, floating-point, and complex values
    • Sequence<T>
    • Function
    • Structs: user-defined record types, one per struct declaration

These are also the names you write in annotations. Generic types take their parameters in angle brackets:

let xs: List<Int> = [1, 2, 3];
let table: Map<String, Int> = %{"a": 1, "b": 2};
let maybe: Option<Any> = Some("hi");
let pair: Tuple<Int, String> = (1, "hi");
let pair2: (Int, String) = (1, "hi");  // tuple shorthand

Nested generics work too — the parser handles the >> ambiguity for you:

let grid: List<List<Int>> = [[1, 2], [3, 4]];

Struct names are type names like any other, including inside generics:

struct Point { x: Int, y: Int }

fn manhattan(p: Point) => p.x + p.y
let points: List<Point> = [Point(1, 2), Point(3, 4)];

A struct name refers to the struct declared earlier in an enclosing scope; it cannot be used before (or inside) its own declaration, and it goes out of scope together with the block or function that declared it. See Struct for the scoping rules.

Note: Any is the base type for every other type, so an Any-annotated binding will accept anything. When a parameter or value has no annotation and the analyser can’t infer a type, it falls back to Any. There is also a Never type used internally for things like break that don’t produce a value — you’ll rarely need to write it by hand.

To go the other way — recover a precise type from an Any-typed value — use a cast: value as List<Int>.