Values and bindings
Every expression has a type. This chapter focuses on how values receive names and how those names behave. Review Basic types first if the primitive types and their literal forms are not yet familiar.
Local inference and annotations
Section titled “Local inference and annotations”The initializer of a local binding usually determines its type:
func main() -> Unit do count = 3; label = "items"; Debug.trace((count, label));endAdd an annotation when it documents intent or supplies information the initializer does not determine:
func main() -> Unit do answer : Int = 42; Console.println(answer.to_string());endAnnotations constrain the value; they do not convert it. The initializer must already have the annotated type.
= introduces an immutable binding
Section titled “= introduces an immutable binding”An ordinary local binding cannot be assigned later:
func subtotal(price : Int, quantity : Int) -> Int do result = price * quantity; resultendA later binding may reuse the same spelling, but it shadows the earlier name instead of changing its storage:
func normalized(value : Int) -> Int do value = if value < 0 then 0 else value end; valueendThe initializer of the second value sees the parameter. Only the new binding
is visible afterward.
mut creates mutable local storage
Section titled “mut creates mutable local storage”Use mut and := when one storage location must change across nested control
flow:
func count_steps(limit : Int) -> Int do mut count = 0; for index = 0; index < limit; index = index + 1 do count := count + 1; end; countendA mutable binding has one fixed type for its entire lifetime. Assignment is a statement, not an expression, and the right-hand side is evaluated before the slot is replaced.
Prefer immutable data and loop state when a value simply evolves from one iteration to the next. Use mutation when nested code must observe the same storage location.
Unused names and explicit discard
Section titled “Unused names and explicit discard”Every binding must be used. Prefix a deliberately unused parameter or binding
with _:
func keep_left(left : Int, _right : Int) -> Int do leftendUse _ = expression; to evaluate and intentionally discard a result without
creating a name.
Module constants
Section titled “Module constants”A constant is immutable, known during compilation, and requires an explicit type:
const MAX_RETRIES : Int = 3const PRODUCT_NAME : String = "Noodle"
func main() -> Unit do Console.println(PRODUCT_NAME); Debug.trace(MAX_RETRIES);endConstant names use uppercase snake case. The current constant-expression subset accepts primitive literals, constant references, and constant arrays; it does not evaluate arbitrary function calls, operators, records, tuples, or datatype constructors during compilation.
Next: functions.