THE JOA LANGUAGE

Scoping Rules and Closures

Understand lexical scope in Joa, block visibility, outer variable updates, and independent closure state.

  • Every { } block creates a new inner scope.
  • Inner scopes can read variables from outer scopes.
  • Inner scopes can re-assign variables declared in outer scopes.
  • Variables declared with let inside a block are not visible outside it.
let x = 10

fn show() {
    let y = 20
    print(x)   // ✓ reads outer x
    print(y)   // ✓ reads local y
}

show()
print(x)       // ✓ still 10
// print(y)    // ✗ Name Error — y is not in scope here

Closure Semantics

Functions use lexical scoping: they capture the environment at the point of their definition, not at the point of their call.

fn make_adder(n) {
    fn adder(x) {
        return x + n    // captures n from make_adder's scope
    }
    return adder
}

let add5 = make_adder(5)
let add10 = make_adder(10)

print(add5(3))    // 8
print(add10(3))   // 13

Each closure has independent state:

fn make_counter(start) {
    let count = start
    fn next() {
        count += 1
        return count
    }
    return next
}

let a = make_counter(0)
let b = make_counter(100)

print(a())   // 1
print(a())   // 2
print(b())   // 101   — independent from a