Skip to content

Bindings: let, let mut and var

In Zolo, every binding starts with let and is immutable by default — the compiler prevents reassignment, eliminating a whole class of aliasing bugs at zero runtime cost:

let creates an immutable binding; the type is inferred from the initial expression.

01-let-immutable.zolo
Playground
// Feature: Immutable binding with `let`
// Syntax: `let name = expression`
// When to use: the default case — every value that does NOT need to
// be reassigned. Immutability by default avoids aliasing bugs.

// `let` creates an immutable binding; the type is inferred.
let name = "Alice"
let age = 30
let pi = 3.14159
let active = true

print(name)  // Alice
print(age)  // 30
print(pi)  // 3.14159
print(active)  // true

// Reassigning a `let` is a compile error (uncomment to test):
// name = "Bob"  // error: cannot assign to immutable binding

// Immutable bindings accept any expression as their initial value.
let total = 10 + 20 * 2
let greeting = "Hello, " + name + "!"
print(total)  // 50
print(greeting)  // Hello, Alice!

When a value needs to change over time — a counter, an accumulator — add mut to the declaration:

let mut allows reassignment and compound-assignment operators (+=, *=, -=).

02-let-mut.zolo
Playground
// Feature: Mutable binding with `let mut`
// Syntax: `let mut name = value` — then `name = new_value`
// When to use: counters, accumulators, any state that changes
// inside loops or branches. The `mut` keyword is required — Zolo
// treats mutability as opt-in to keep intent visible.

// Mutable: needs `mut` in the declaration.
let mut counter = 0
counter = counter + 1
counter = counter + 1
counter = counter + 1
print(counter)  // 3

// Classic accumulator in a loop.
let mut sum = 0
for i in 1..=10 {
  sum = sum + i
}
print(sum)  // 55

// Reassignment respects the type inferred at declaration.
let mut score: int = 100
score = 90
score = 80
print(score)  // 80

// Compound assignment — equivalent to `x = x + 5`.
let mut x = 10
x += 5
x *= 2
x -= 1
print(x)  // 29

var is an exact alias for let mut. Use var when you want to save keystrokes or when you plan to use storage classes (var<lazy>, var<persistent>, etc.) — the <...> syntax only exists on var:

var and let mut are interchangeable; only var accepts storage modifiers.

11-var-alias.zolo
Playground
// Feature: `var` — alias for `let mut`
// Syntax: `var name = value` — then `name = new_value`
// When to use: anywhere `let mut` is valid. Same semantics, fewer
// keystrokes, and the gateway to storage classes (`var<...>`).
// `let mut` continues to work; both are accepted indefinitely.

// Mutable binding via the new shorter form.
var counter = 0
counter = counter + 1
counter = counter + 1
counter = counter + 1
print(counter)  // 3

// Equivalent in every way to `let mut counter = 0`.
let mut also_counter = 0
also_counter = also_counter + 1
print(also_counter)  // 1

// Compound assignment works the same.
var score = 100
score += 10
score -= 5
print(score)  // 105

// Type annotations also work.
var ratio: float = 0.5
ratio = ratio * 2
print(ratio)  // 1.0

// Why prefer `var`? Storage classes use the `var<...>` syntax —
// see 12-var-lazy.zolo, 13-var-persistent.zolo, etc. `let mut` does
// not accept storage classes (would be ambiguous to parse).

Challenge

Declare an accumulator var total = 0 and add the numbers from 1 to 100 with a for loop. Confirm that the result is 5050.

enespt-br