9. Block Expressions
Block expressions allow grouping multiple statements together and returning a value from the final expression. They create a new scope for variable declarations and enable sequential execution with proper scoping rules.
block_expression := '{' statement* expression? '}'
Examples:
// Simple block with local variables
let result = {
let x = 10
let y = 20
x + y
}
print("Result: ${result}") // prints "Result: 30"
// Nested blocks
let complex = {
let outer = 100
let inner_result = {
let inner = 50
outer + inner
}
inner_result * 2
}
print("Complex: ${complex}") // prints "Complex: 300"
// Block with function calls
fn multiply(a: int, b: int) -> int = a * b
let calc = {
let a = 5
let b = 6
multiply(a: a, b: b)
}
print("Calculation: ${calc}") // prints "Calculation: 30"
9.1 Block Scoping Rules #
Block expressions create a new lexical scope:
- Variables declared inside a block are only visible within that block
- Variables from outer scopes can be accessed (lexical scoping)
- Variables declared in a block shadow outer variables with the same name
- Variables go out of scope when the block ends
Scoping Examples:
let x = 100
let result = {
let x = 50 // Shadows outer x
let y = 25 // Only visible in this block
x + y // Uses inner x (50)
}
print("Result: ${result}") // 75
print("Outer x: ${x}") // 100 (unchanged)
// print("${y}") // ERROR: y not in scope
9.2 Block Return Values #
Block expressions return the value of their final expression:
- If the block ends with an expression, that value is returned
- If the block has no final expression, it returns the unit type
- The block’s type is determined by the type of the final expression
9.3 Performance Characteristics #
Block expressions are zero-cost abstractions:
- Compile-time scoping: All variable scoping resolved at compile time
- No runtime overhead: Blocks compile to sequential instructions
- Stack allocation: Local variables allocated on the stack
- Optimized away: Simple blocks with no local variables are optimized away