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
letinitializer 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'slet— 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 theletitself.
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;
};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
tryturns it intonone. - An assertion is also the last statement of a
contractbody, 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))) // => noneAn 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
};