Collections
Tova provides powerful collection types -- arrays and objects -- with Python-inspired features like comprehensions, slicing, and membership testing.
Arrays
Create arrays with square brackets:
numbers = [1, 2, 3, 4, 5]
names = ["Alice", "Bob", "Carol"]
mixed = [1, "two", true, nil]
empty = []Accessing Elements
Use zero-based indexing:
fruits = ["apple", "banana", "cherry"]
first = fruits[0] // "apple"
last = fruits[-1] // "cherry" (negative index counts from end)Common Array Methods
items = [3, 1, 4, 1, 5, 9]
len(items) // 6
items.push(2) // [3, 1, 4, 1, 5, 9, 2]
items.map(fn(x) x * 2) // [6, 2, 8, 2, 10, 18]
items.filter(fn(x) x > 3) // [4, 5, 9]
items.reduce(fn(a, b) a + b, 0) // 23
items.sort() // [1, 1, 3, 4, 5, 9]
items.reverse() // [9, 5, 1, 4, 1, 3]
items.includes(4) // true
items.indexOf(5) // 4
items.join(", ") // "3, 1, 4, 1, 5, 9"Objects
Create objects with key-value pairs in curly braces:
user = {
name: "Alice",
age: 30,
email: "alice@example.com"
}Accessing Properties
user.name // "Alice"
user["age"] // 30 (bracket notation for dynamic keys)Shorthand Properties
When the variable name matches the key name, you can use shorthand:
name = "Alice"
age = 30
// Instead of:
user = { name: name, age: age }
// You can write:
user = { name, age }Computed Property Keys
Use bracket notation for dynamic keys:
field = "email"
data = { [field]: "alice@example.com" }
// { email: "alice@example.com" }Spread Operator
The ... operator spreads arrays and objects into new collections.
Array Spread
a = [1, 2, 3]
b = [4, 5, 6]
combined = [...a, ...b] // [1, 2, 3, 4, 5, 6]
with_extra = [0, ...a, 99] // [0, 1, 2, 3, 99]Object Spread
defaults = { host: "localhost", port: 8080, debug: false }
overrides = { port: 3000, debug: true }
config = { ...defaults, ...overrides }
// { host: "localhost", port: 3000, debug: true }This is the standard way to create updated copies of immutable objects:
fn update_name(user, new_name) {
{ ...user, name: new_name }
}List Comprehensions
Tova supports Python-style list comprehensions for concise collection transformations:
squares = [x * x for x in range(10)]
// [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]With Filtering
Add an if clause to filter:
evens = [x for x in range(20) if x % 2 == 0]
// [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
short_names = [name for name in names if len(name) <= 4]With Transformation and Filtering
processed = [x * 2 for x in range(10) if x > 0]
// [2, 4, 6, 8, 10, 12, 14, 16, 18]
upper_long = [name.upper() for name in names if len(name) > 3]Nested Comprehensions
pairs = [[x, y] for x in range(3) for y in range(3)]
// [[0,0], [0,1], [0,2], [1,0], [1,1], [1,2], [2,0], [2,1], [2,2]]Dict Comprehensions
Build objects from key-value pairs:
squares_map = {x: x * x for x in range(5)}
// {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}entries = [["name", "Alice"], ["age", "30"]]
obj = {k: v for k, v in entries}
// { name: "Alice", age: "30" }Slicing
Tova supports Python-style array slicing with [start:end:step] syntax:
Basic Slicing
items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
items[2:5] // [2, 3, 4] start at 2, end before 5
items[:3] // [0, 1, 2] first 3 elements
items[7:] // [7, 8, 9] from index 7 to end
items[-3:] // [7, 8, 9] last 3 elementsSlicing with Step
items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
items[::2] // [0, 2, 4, 6, 8] every other element
items[1::2] // [1, 3, 5, 7, 9] odd-indexed elements
items[::3] // [0, 3, 6, 9] every third elementReversing with Slicing
items = [1, 2, 3, 4, 5]
items[::-1] // [5, 4, 3, 2, 1] reverse the array
items[4:1:-1] // [4, 3, 2] reverse a portionString Slicing
Slicing works on strings too:
text = "Hello, World!"
text[0:5] // "Hello"
text[7:] // "World!"
text[::-1] // "!dlroW ,olleH"Chained Comparisons
Tova supports Python-style chained comparisons:
if 1 < x < 10 {
print("x is between 1 and 10")
}
if 0 <= score <= 100 {
print("Valid score")
}
if a < b < c < d {
print("Strictly increasing")
}This is more readable than if x > 1 and x < 10.
Membership Testing
Use in to check whether an element exists in a collection:
fruits = ["apple", "banana", "cherry"]
if "apple" in fruits {
print("Found apple!")
}
if "mango" not in fruits {
print("No mango")
}Works with strings too:
if "@" in email {
print("Looks like an email")
}
if "admin" in username {
print("Admin user detected")
}And with objects (checks keys):
config = { host: "localhost", port: 8080 }
if "host" in config {
print("Host is configured")
}Practical Tips
Use comprehensions for transformations. They are more concise and often more readable than .map() and .filter() chains:
// Comprehension style:
active_names = [user.name for user in users if user.active]
// Method chain style:
active_names = users.filter(fn(u) u.active).map(fn(u) u.name)Both are valid; use whichever reads better for your case.
Slicing is non-destructive. It always returns a new array or string, leaving the original unchanged. This pairs well with immutable-by-default semantics.
Spread for immutable updates. Since variables are immutable by default, use spread to create "updated" copies:
fn add_item(cart, item) {
{ ...cart, items: [...cart.items, item] }
}
fn remove_first(list) {
let [_, ...rest] = list
rest
}Chained comparisons for range checks. Instead of if age >= 13 and age <= 19, write if 13 <= age <= 19. It reads like math and is less error-prone.