Skip to main content

block, let, assert

A braced sequence of statements ending in one tail expression. The tail expression's type is the block's type, so a block is an expression and goes wherever an expression goes.

function sweep_area(diameter: float64) -> float64 = {
    let radius: float64 = diameter / 2.0;
    assert radius > 0.0;
    3.14159 * radius * radius;
};

Blocks​

A block holds zero or more statements and then one expression, semicolon-terminated. A statement is a let, an assert, or a statement if.

  • The smallest block is { expr; }. There is no empty block.
  • Blocks nest, and a nested block is an ordinary expression — a let initializer may be one.
  • A declaration cannot appear in a block. Structs, functions, and consts are top-level only.
  • A name a block binds is gone at the closing brace.
function offset_range(raw_range_m: float64) -> float64 = {
    let corrected: float64 = { let bias_m: float64 = 12.5; raw_range_m - bias_m; };
    corrected * 2.0;
};

let​

let binds an expression to a name, to _, or to a tuple pattern. The type annotation is optional.

  • The name is in scope for the statements that follow it and for the tail expression. It is not in scope in its own initializer.
  • Bindings are immutable, and nothing reassigns one.
  • A name already in scope — a parameter, an earlier let, an enclosing block's let — cannot be bound again.
  • With an annotation, the initializer must be assignable to it.
  • Without an annotation, the initializer's type must be fixed at every position. An unpinned numeric literal, a bare none, or an empty array anywhere inside it leaves nothing to infer from.
  • A tuple pattern needs one element per element of the tuple. _ discards without binding and may appear more than once.
  • A tuple element may carry its own : Type. The annotation after the pattern belongs to the let itself.
function correct(fix: (float64, float64), raw_bearing_deg: float64) -> float64 = {
    let (lat_deg, _) = fix;
    let declination_deg = 7.5: float64;
    let bearing_deg: float64 = raw_bearing_deg + declination_deg;
    bearing_deg + lat_deg;
};
let Is Not a Top-Level Declaration

A let binds a name inside a block, whereas the form that binds at module scope is a const.

assert​

assert evaluates a condition and stops the enclosing block when it is false. It is a statement, not an expression, and produces no value.

  • The condition has type bit.
  • A failed assertion is a catchable failure, so try turns it into none.
  • An assertion is also the last statement of a contract body, where it states the property being verified rather than guarding a computation.
function halve(d: float64) -> float64 = { assert d > 0.0; d / 2.0; };

halve((10.0: float64))         // => 5
try(halve((10.0: float64)))    // => some(5)
try(halve((-1.0: float64)))    // => none
Assertions Always Run

An assert is part of what the program means, not a debug check a build mode removes. A failed one is a value the program produces — try turns it into none — so dropping it would change the result.

Errors​

Binding a name that is already in scope​

function report(track_id: uint32) -> uint32 = {
    let track_id: uint32 = 7;   // error: illegal-shadowing
    track_id;
};

An initializer with no type of its own​

function ready(armed: bit) -> bit = {
    let threshold = 5;   // error: ambiguous-inferred-type
    armed;
};

A tuple pattern of the wrong length​

function latitude(fix: (float64, float64, float64)) -> float64 = {
    let (lat_deg, lon_deg) = fix;   // error: tuple-pattern-arity
    lat_deg;
};

An assertion whose condition is not bit​

function halve(d: float64) -> float64 = {
    assert "positive";   // error: assert-condition-type
    d / 2.0;
};

Naming a binding outside the block that made it​

function offset_range(raw_range_m: float64) -> float64 = {
    let corrected: float64 = { let bias_m: float64 = 12.5; raw_range_m - bias_m; };
    corrected - bias_m;   // error: unresolved-reference
};
  • if — the statement that also belongs in a block, and the expression that has a value.
  • try — what a failed assertion becomes.
  • literals — the ascription that gives an initializer a type.