Variables & Scope

Master variable declaration, scope management, and mutability control in Topaz. Learn the differences between let, let mut, const and understand lexical scoping and closure behavior.

Variables and scope are fundamental to programming. Topaz provides a modern and safe variable system that reduces bugs and increases code clarity. 🎯

🔧 Variable Declaration

let - Immutable Variables (Default)

// All variables are immutable by default
let name = "John Topaz"
let age = 25
let isActive = true

// Cannot be changed once defined
// name = "Different Name"  // Compile error!

let mut - Mutable Variables

// Use let mut when you need to change values
let mut score = 100
let mut message = "Initial message"

// Value can be changed
score = 95
message = "Updated message"

print("Current score: {score}")  // "Current score: 95"

const - Compile-time Constants

// Constants determined at compile time
const MAX_SIZE: int = 1000
const APP_NAME: string = "Topaz Calculator"
const PI: float = 3.141592653589793

// Global constants available in other files
const defaultConfig = {
    language: "English",
    theme: "dark",
    autoSave: true
}

🎯 Type Inference and Explicit Types

// Type inference (recommended)
let inferredNumber = 42           // int
let inferredString = "Hello"      // string
let inferredArray = [1, 2, 3]     // [int]

// Explicit types (when needed)
let explicitNumber: int = 42
let explicitString: string = "Hello"
let explicitArray: [int] = [1, 2, 3]

// Explicit types help with complex cases
let userData: { name: string, age: int } = {
    name: "Developer Kim",
    age: 30
}

🏠 Scope Rules

Global Scope

// File top level - global scope
let globalVariable = "Accessible everywhere"
const globalConstant = 100

function useAnywhere() {
    print(globalVariable)  // Accessible
    print(globalConstant)  // Accessible
}

Function Scope

function calculator(a: int, b: int) -> int {
    // Function parameters also belong to function scope
    let result = a + b        // Function scope
    let mut tempValue = a * 2     // Function scope
    
    print("Result: {result}")
    return result
}

// print(result)  // Error! Cannot access outside function

Block Scope

function scopeExample() {
    let outerVariable = "outside"
    
    if true {
        let innerVariable = "inside"      // Block scope
        let mut blockMutable = 10            // Block scope
        
        print(outerVariable)             // Accessible (outer scope)
        print(innerVariable)             // Accessible (same scope)
    }
    
    print(outerVariable)                 // Accessible
    // print(innerVariable)              // Error! Cannot access outside block
}

Loop Scope

// for loop variables have loop scope
for i in 0..5 {
    let loopVariable = i * 2
    print("i: {i}, value: {loopVariable}")
}

// print(i)  // Error! Cannot access outside loop

// while loops work the same way
let mut counter = 0
while counter < 3 {
    let temp = counter + 10
    print(temp)
    counter += 1
}

🔒 Variable Shadowing

let name = "John Topaz"

function shadowingExample() {
    let name = "Developer Park"        // Shadows global name
    print("In function: {name}")       // "In function: Developer Park"
    
    {
        let name = "Designer Lee"       // Shadows function name
        print("In block: {name}")       // "In block: Designer Lee"
    }
    
    print("After block: {name}")       // "After block: Developer Park"
}

print("Global: {name}")               // "Global: John Topaz"
shadowingExample()

🌟 Closures and Scope

function createCounter(initialValue: int) -> function() -> int {
    let mut count = initialValue           // Captured by closure
    
    return function() -> int {
        count += 1                     // Access outer scope variable
        return count
    }
}

let counter1 = createCounter(0)
let counter2 = createCounter(100)

print(counter1())  // 1
print(counter1())  // 2
print(counter2())  // 101
print(counter1())  // 3

🎭 Pattern Matching and Variables

let user = {
    name: "John Topaz",
    age: 28,
    job: "Developer"
}

// Destructuring creates new variables
let { name, age } = user
print("Name: {name}, Age: {age}")

// Variable binding in match
match user.age {
    case age if age < 20 => print("Likely a student")
    case age if age < 30 => print("Young professional")
    case age => print("Experienced: {age} years old")
}

// Array pattern matching
let scores = [95, 87, 92, 88]
match scores {
    case [first, second, ...rest] => {
        print("First: {first}")
        print("Second: {second}")
        print("Rest: {rest}")
    }
}

⚡ Advanced Scope Patterns

IIFE (Immediately Invoked Function Expression)

// Immediately invoked function for scope isolation
let result = (function() -> int {
    let tempCalculation = 10 * 20
    let adjustment = 50
    return tempCalculation + adjustment
})()

print(result)  // 250
// tempCalculation, adjustment are not accessible

Module Scope

// math_utils.tpz file
export const PI = 3.141592653589793
export let defaultPrecision = 6

export function circleArea(radius: float) -> float {
    let internalCalc = radius * radius    // Only accessible within module
    return PI * internalCalc
}

// main.tpz file
import { PI, circleArea } from "./math_utils"

print(PI)            // Accessible
print(circleArea(5.0))   // Accessible
// print(internalCalc)   // Error! Module internal variable not accessible

🛡️ Best Practices

1. Use Immutable by Default

// Good: Use let by default
let config = {
    theme: "dark",
    language: "English"
}

// Use let mut only when necessary
let mut progressStatus = "started"
progressStatus = "in-progress"
progressStatus = "completed"

2. Meaningful Variable Names

// Bad example
let a = userList.length()
let b = a * 0.1

// Good example  
let totalUsers = userList.length()
let commission = totalUsers * 0.1

3. Minimize Scope

function processData(rawData: [int]) -> [int] {
    let result = []
    
    for item in rawData {
        // Variables needed only in loop should be declared in loop
        let transformedValue = item * 2 + 1
        let validatedValue = if transformedValue > 0 { transformedValue } else { 0 }
        
        result.push(validatedValue)
    }
    
    return result
}

🔍 Debugging Tips

// Utility for scope debugging
function printScopeInfo() {
    let localVariable = "local"
    
    print("Variables in current function:")
    print("- localVariable: {localVariable}")
    
    // Access global variables
    if defined(globalVariable) {
        print("- Global variable accessible")
    }
}

// Check variable existence
function safeAccess(variableName: string) {
    if defined(variableName) {
        print("{variableName} is defined")
    } else {
        print("{variableName} is not defined")
    }
}

Topaz's variable and scope system provides both safety and clarity. With immutability as default but allowing mutability when needed, and clear scope rules, you can write predictable code. 🚀