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
Destructuring pulls values out of objects and arrays into individual variables. No keyword prefix is needed -- just write the pattern on the left side of the assignment.
TIP
Tova does not use let for destructuring or for anything else. Use plain x = 10 for immutable bindings, var x = 10 for mutable ones, and { a, b } = obj for destructuring.
Object Destructuring
Extract fields from an object by name:
person = { name: "Alice", age: 30, email: "alice@example.com" }
{ name, age } = person
print(name) // "Alice"
print(age) // 30You can rename the bindings:
{ name: userName, age: userAge } = person
print(userName) // "Alice"
print(userAge) // 30Array Destructuring
Pull elements out of arrays by position:
coords = [10, 20, 30]
[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]
[first, ...rest] = items
print(first) // 1
print(rest) // [2, 3, 4, 5]
[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] }
}
{ user: { name, scores: [first_score, ...other_scores] } } = data
print(name) // "Alice"
print(first_score) // 95
print(other_scores) // [87, 92]Type Annotations
Type annotations in Tova are used on function parameters and return types, not on variable declarations. Tova infers variable types from their assigned values:
// Variables — types are inferred automatically
x = 42 // inferred as Int
name = "Alice" // inferred as String
is_active = true // inferred as Bool
scores = [90, 85, 92] // inferred as [Int]Use type annotations on function signatures for documentation and type checking:
fn add(a: Int, b: Int) -> Int {
a + b
}
fn greet(name: String) -> String {
"Hello, {name}!"
}Practical 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:
{ 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 using a tuple
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])