Variables
Tova takes a clear stance on mutability: variables are immutable by default. If you want a variable that can change, you opt in explicitly with var. This makes your code easier to reason about and helps prevent accidental mutations.
Immutable Variables
A plain assignment creates an immutable binding. Once set, it cannot be reassigned:
name = "Alice"
age = 30
pi = 3.14159Attempting to reassign an immutable variable produces a compile-time error:
name = "Alice"
name = "Bob" // Error: cannot reassign immutable variable 'name'Mutable Variables
Use var when you need a variable that can change over time:
var count = 0
count += 1 // OK
count = count + 5 // OK
var name = "Alice"
name = "Bob" // OK — var allows reassignmentMutable variables support all compound assignment operators:
var x = 10
x += 5 // x is now 15
x -= 3 // x is now 12
x *= 2 // x is now 24
x /= 4 // x is now 6Multiple Assignment
Tova supports assigning multiple variables in a single statement:
a, b = 1, 2
x, y, z = "hello", true, 42This is particularly handy for swapping values without a temporary variable:
var a = 1
var b = 2
a, b = b, a // a is now 2, b is now 1Destructuring with let
The let keyword is used specifically for destructuring -- pulling values out of objects and arrays into individual variables.
TIP
In Tova, let is only for destructuring. It is not used for variable declaration like in JavaScript. Use plain x = 10 for immutable bindings or var x = 10 for mutable ones.
Object Destructuring
Extract fields from an object by name:
person = { name: "Alice", age: 30, email: "alice@example.com" }
let { name, age } = person
print(name) // "Alice"
print(age) // 30You can rename the bindings:
let { name: userName, age: userAge } = person
print(userName) // "Alice"
print(userAge) // 30Array Destructuring
Pull elements out of arrays by position:
coords = [10, 20, 30]
let [x, y, z] = coords
print(x) // 10
print(y) // 20Rest Patterns
Use the spread operator ... to capture remaining elements:
items = [1, 2, 3, 4, 5]
let [first, ...rest] = items
print(first) // 1
print(rest) // [2, 3, 4, 5]
let [head, second, ...tail] = items
print(head) // 1
print(second) // 2
print(tail) // [3, 4, 5]Nested Destructuring
Destructuring can go multiple levels deep:
data = {
user: { name: "Alice", scores: [95, 87, 92] }
}
let { user: { name, scores: [first_score, ...other_scores] } } = data
print(name) // "Alice"
print(first_score) // 95
print(other_scores) // [87, 92]Type Annotations
You can optionally annotate variables with types. Tova uses the : Type syntax after the variable name:
x: Int = 42
name: String = "Alice"
is_active: Bool = true
scores: [Int] = [90, 85, 92]Type annotations serve as documentation and enable better tooling support. Tova's type checker will warn you if the assigned value does not match the declared type.
var count: Int = 0
count += 1 // OK — still an Int
pi: Float = 3.14159
ratio: Float = 22.0 / 7.0Practical Tips
Prefer immutable by default. Only reach for var when the value genuinely needs to change -- loop counters, accumulators, state that evolves over time. Immutable bindings make code easier to follow and less prone to bugs.
Use destructuring to keep code clean. Instead of accessing response.data.user.name repeatedly, destructure it once:
let { data: { user: { name, email } } } = response
// Now use 'name' and 'email' directlyMultiple assignment shines for swaps and coordinate work:
// Swap without a temp variable
var left = "hello"
var right = "world"
left, right = right, left
// Return multiple values from a function
fn min_max(items) {
min_val = items |> min()
max_val = items |> max()
min_val, max_val
}
lo, hi = min_max([3, 1, 4, 1, 5, 9])