-
-
Notifications
You must be signed in to change notification settings - Fork 0
Values Bindings And Blocks
Pudu distinguishes the name of a value from a place whose value may change. let introduces the
former; var introduces the latter. The distinction is lexical and visible at the declaration.
let origin = 0
var position = origin
position = position + 1
The inferred type of both names is Int. Reassigning origin is invalid. Assignment changes an
existing mutable binding and produces (); it does not create a new name.
Local bindings normally infer their type from the initializer. An annotation constrains that inference and records an intended boundary.
let port: UInt16 = 8080
var retries: Int = 3
Integer literals are held without loss while inference is active. 8080 can therefore be checked
as UInt16; a value outside that type's range is rejected instead of truncated. Pudu does not use
an annotation as a request for a lossy conversion.
A name is visible after its declaration in the containing block. An inner block may shadow it. The inner binding ends with the block and does not alter the outer one.
fn adjusted(value: Int) -> Int {
let offset = 2
let result = {
let offset = 5
value + offset
}
result + offset
}
adjusted(10) evaluates to 17: the inner block produces 15, then the outer offset adds two.
A block evaluates its statements in source order. If the last item is an expression, that value
is the value of the block. A block with no trailing expression produces ().
let magnitude = {
let squared = value * value
squared
}
The absence of a terminator is meaningful here: squared is the result, not a discarded
statement. This rule is shared by function bodies, conditional branches, match arms written as
blocks, loop bodies, and macro block arguments.
Only a var binding can be assigned. Mutation of a collection held by a var is expressed by an
assignment or a mutating built-in method supported for that value.
fn sum(values: Array[Int]) -> Int {
var total = 0
for value in values { total = total + value }
total
}
Pudu does not infer mutability from later use. Changing total from var to let changes the
program's static meaning and is diagnosed at the assignment.
Names introduced inside an if, match arm, or loop body do not escape that region. Pattern
bindings follow the same rule. let … else is the exception with an explicit purpose: after its
fallback diverges, the successful pattern bindings remain available in the containing block.
fn require(value: Option[Int]) -> Int {
let Some(found) = value else { return 0 }
found
}
The fallback cannot fall through, so every path reaching found has proved the Some pattern.
- Modules And Imports
- Values, Bindings, And Blocks
- Types And Inference
- Records, Sums, And Tuples
- Functions Generics And Traits
- Pattern Language
- Control Flow And Patterns
- Iteration And Loops
- Failure And Propagation
- Numbers And Collections
- Sets Maps And Sequences
- Keyed Structures
- Trees And Hierarchies
- Standard Library
- Output Formatting And Testing
- Tasks And Scopes
- Compile-Time Evaluation
- Typed Macros
- References And Unsafe
- Worked Programs