Skip to content

Variables

Almide has two ways to bind values: let for immutable bindings and var for mutable bindings.

let creates an immutable binding. Once bound, the value cannot be changed:

let name = "Alice"
let age = 30
let active = true

Attempting to reassign a let binding is a compile error:

let x = 1
x = 2 // Compile error: cannot reassign immutable binding

var creates a mutable binding that can be reassigned:

var count = 0
count = count + 1
count = count + 1
println(int.to_string(count)) // "2"

Mutable bindings also enable in-place operations on collections:

var items = ["a", "b", "c"]
items[0] = "A" // index write
list.push(items, "d") // push to end
var scores: Map[String, Int] = [:]
scores["alice"] = 100 // map write

Use var sparingly. Prefer let unless mutation is necessary.

Type annotations are optional when the type can be inferred:

let x = 42 // inferred as Int
let y: Int = 42 // explicit annotation

Annotations are required when the type cannot be inferred, such as empty collections:

let xs: List[String] = []
let m: Map[String, Int] = [:]

The annotation syntax is name: Type:

let pair: (Int, String) = (1, "one")
let f: Fn(Int) -> Int = (x) => x + 1

Record destructuring extracts fields into individual bindings:

type User = { name: String, age: Int }
let user = { name: "Alice", age: 30 }
let { name, age } = user
println(name) // "Alice"

Destructuring rules:

  • Only one level deep (no nested destructuring)
  • No renaming (field names become variable names)
  • let only (no var destructuring)

let at the top level creates module-scope constants:

let PI = 3.14159265358979323846
let MAX_RETRIES = 3
let GREETING = "Hello, world"

By convention, top-level constants use UPPER_CASE names. They are evaluated at compile time when possible.

A new let binding can shadow an existing binding in the same scope:

let x = 1
let x = x + 1 // shadows the previous x
println(int.to_string(x)) // "2"

Shadowing creates a new binding rather than mutating the existing one.

Use _ to discard a value you don’t need:

let _ = some_function() // call for side effect, discard result

In for loops, _ ignores the loop variable:

for _ in 0..5 {
println("tick")
}

In tuple destructuring within for:

for (_, v) in map.entries(config) {
println(v)
}