Variables & Scope

TLang uses let bindings. Every binding is immutable — once declared, a name holds its value until the scope ends. To update a value, rebind the name with a new let.

let Bindings

Declare a variable with let. The type annotation is optional; the compiler infers it from the value.

      let name  = "Alice"
      let count = 0
      let pi    = 3.14159
      let items = List.of("a", "b", "c")
    

With explicit type annotation.

      let name:  String = "Alice"
      let count: Int    = 0
    

Rebinding

let does not mutate — it shadows the previous binding with a new one. The old binding still exists in the outer scope but is hidden.

      let count = 0
      let count = count + 1   // shadows previous — count is now 1
      let count = count + 1   // count is now 2
    

This is the standard pattern for accumulating a value in a loop.

      let result = ""
      for (item in items) {
          let result = result + item + "\n"
      }
      return result
    

Scope

Bindings are lexically scoped to the block they are declared in. An inner binding shadows an outer one with the same name; the outer binding is restored when the block exits.

      let x = 10

      if (condition) {
          let x = 20      // shadows outer x inside this block
          Terminal.println(x)   // prints 20
      }

      Terminal.println(x)       // prints 10 — outer x restored
    

Function bodies are their own scope.

      func compute(n: Int): Int {
          let x = n * 2    // local to this function
          let x = x + 1
          return x
      }

      // x is not accessible here
    

Rebinding in for Loops

Inside a for loop body, rebinding a variable declared before the loop updates the outer binding. This is the correct accumulator pattern.

      let total = 0
      for (n in List.of(1, 2, 3)) {
          let total = total + n    // updates the outer total each iteration
      }
      // total is now 6
    

Contrast with while loops, where rebinding behaves the same (shadows within body, restores on exit).

Expression if

if can be used as an expression, assigning its result to a let. The else branch is mandatory.

      let label = if (n < 0) "negative" else if (n == 0) "zero" else "positive"
    
      let status = if (ok) "success" else "failure"
      return if (debug) "verbose: " + result else result
    

match Expression

match can also be used as an expression. A default case is required.

      let mime = match (ext) {
          case "html" => "text/html"
          case "json" => "application/json"
          case "css"  => "text/css"
          default     => "text/plain"
      }