TYCHO Search

Search documentation

    ↑ ↓ to move · Enter to open · Esc to close

    12. Declarations, bindings, and scoping

    This chapter defines how names are introduced, assigned, resolved, and scoped within function bodies. The grammar of each form is in §4.3.1; this chapter gives their meaning.

    12.1 Binding forms

    FormMeaning
    x := eDeclare x, inferring its type from e (§6.3). e MUST NOT be void.
    x : T = eDeclare x with explicit type T; e is checked against T.
    x = eAssign to an existing variable x; e is checked against x's type.
    p = eAssign through a place p (index, field, tuple element, or subscript).
    p op= eCompound assignment (§12.4).
    a, b := f()Declare a, b from a tuple-valued RHS (2–8 targets).
    a, b = f()Assign to existing a, b from a tuple-valued RHS.
    (p, q) = eAssign a tuple-valued RHS into 2–8 places, all right-hand sides first (§17.5).

    A := or x : T = declares a new variable; a program MUST NOT declare a name already visible in the same block (a nested block may shadow it, §12.3). An = or place-assignment requires the target to already denote a mutable variable or place; assigning to an undeclared name is an error. A declaration's value form may be an expression or a tail-position value if/match (§13.5, §14.3).

    12.2 Constants

    const NAME = ConstExpr
    

    A const binds a name to a compile-time constant, at module top level or inside a function body. The right-hand side MUST fold to a single literal at compile time: a literal, or (for the folding forms) integer arithmetic, bitwise, and unary operations over literals, + over two string literals, and — at top level — backward references to earlier top-level constants. The string form means a constant such as const TERM = "\r\n" + "\r\n" is one literal, not a run-time concatenation (§3.9.4). A const is an immutable named literal, folded at each use site (it has no run-time storage). Reassigning a const, or a non-constant right-hand side, is an error. A top-level const MUST NOT collide with a struct, enum, newtype, handle, variant, function, or another constant of the same name. Float arithmetic in a const right-hand side, and a *local* const referencing another const, are not permitted (only negative float *literals* fold).

    12.3 Scope and shadowing

    Tycho has lexical block scope. A variable is visible from its declaration to the end of the block that contains it, and each if/elif/else arm, each match arm, each loop body, and each select arm is a block. A match arm's payload bindings are visible only within that arm.

    A declaration in a nested block MAY shadow a name from an enclosing block; within the nested block the inner binding is the one in scope, and the outer binding is unaffected after the block ends. A counting-for loop variable is scoped to the loop body. Because Tycho has value semantics, a shadowing declaration takes an independent copy; mutating the inner name never affects the shadowed outer value.

    12.4 Compound assignment

    p op= e means p = p op e for op in + - * / % & | ^ << >>, on any place p (variable, index, field, tuple element). The place is evaluated for the read and the store; a side-effecting call inside the place is evaluated once (the implementation hoists it), while a pure index sub-expression may be evaluated twice — exactly as writing the assignment out longhand would (§13.4; resolved by probing, both compilers agree).

    12.5 Name resolution

    Within a body, an identifier resolves to (in order) a local variable or parameter in scope, a top-level constant, a nullary enum variant, or a function/type name. A qualified name pkg.name resolves through the imported package's prefix (§28). All package-level names are mangled to one flat namespace by a per-package prefix; this mangling is not observable in the source language (a program always uses the unqualified or pkg.-qualified spelling).