Skip to main content

Variables & Scope

Variables don't need to be declared; assigning to a new name creates it.

foo = 5
foo = foo + 1

Variables don't have fixed types, either – a variable can go from holding a number to holding a string.

bar = 17
bar = "hello, world"

Where assignments land

Assignment x = ... modifies an existing variable within the current function, if there is one. Otherwise it creates a new variable in the nearest { ... } brackets:

{
x = 1
{ x = 2, y = 3 }
show x # x = 2
show y # Error: `y` is not defined
}

Here the inner x = 2 updates the outer x, because one already exists. But y is created fresh in the inner block, and doesn't exist outside it.

This rule means variables can't "leak" out of conditionals in a way that depends on runtime values. If you want to initialise a variable inside an if or a loop, give it a value first:

x = nil
if condition {
x = 2
}

Now x is defined either way.

Functions are boundaries

A function can never alter a variable outside its own body. It's free to use an outer name, but assigning to it creates a fresh, local variable:

x = 1
fn foo() {
x = 2
show x
}
foo() # x = 2
show x # x = 1

If you actually want a function to change a caller's variable, that's what swap arguments are for.

let

let overrides the default and creates a new variable, regardless of whether one already exists. The new variable shadows the old one for the duration of the let block:

{
x = 1
let x = 2 { show x } # x = 2
show x # x = 1
}

let is also the entry point to pattern matching in conditionals – if let Some(x) = m { ... } – which we'll come to later.