Walrus/Language

Collections

Working with Arrays and Objects in Walrus.

Updated April 17, 20266 min readWalrus / Collections / Arrays / Objects

Walrus provides two primary runtime collection types for managing groups of data: Arrays and Objects. Both are heap-allocated and follow move semantics. Walrus also exposes higher-level core abstractions such as Map, Tuple, container, and view<T> directly in the language/runtime model.


Arrays

Arrays are ordered, integer-indexed collections of values. They are mutable and can grow dynamically.

Creating Arrays

Use square brackets [] to create an array literal.

Walrus
let scores = [10, 20, 30]
let mixed = [1, "two", 3.0] # Arrays can store mixed types

Accessing Elements

Access elements using zero-based integer indexing.

Walrus
let values = [100, 200, 300]
let first = values[0]
values[1] = 500 # Mutation

Array Methods

Walrus provides several built-in functions for manipulating arrays:

FunctionDescription
len(array) -> intReturns the number of elements in the array.
push(array, value) -> intAppends a value to the end of the array. Returns the new length.
pop(array) -> anyRemoves and returns the last element of the array.
removeAt(array, index)Removes the element at the specified index.
removeValue(array, value) -> boolRemoves the first occurrence of value. Returns true if found.
Walrus
let list = [1, 2]
push(list, 3)     # list is [1, 2, 3]
let last = pop(list) # last is 3, list is [1, 2]
removeAt(list, 0)  # list is [2]

Objects

Objects are collections of named properties. They behave like string-keyed maps or "plain old data" structures.

Creating Objects

Use curly braces {} to create an object literal.

Walrus
let player = { name = "Hero", hp = 100, x = 10 }

Accessing Properties

Use dot syntax . to read or write properties.

Walrus
let current_hp = player.hp
player.x = player.x + 5

Property Shorthand

If you have a local variable with the same name as a property, you can use the shorthand syntax:

Walrus
let name = "Walrus"
let hp = 50
let enemy = { name, hp } # Equivalent to { name = name, hp = hp }

Object Built-ins

FunctionDescription
len(object) -> intReturns the number of properties currently in the object.
delete(object, key)Removes a property from the object by its name (string).
Walrus
let state = { score = 10, active = true }
delete(state, "active") # state now only has 'score'

Performance and Ownership

  1. Heap Allocated: Both arrays and objects live on the heap and are managed by reference counting.
  2. Move Semantics: Assigning a collection to a new variable moves ownership. The original variable becomes invalid.
  3. No Borrowed Storage: You cannot store a ref or mut ref inside an array or object. Collections must always own their contents.

Core Containers

Use the built-in container helpers when you want a more structured API than raw arrays and objects:

Walrus
let stats = mapOf<string, int>()
stats.set("hp", 120)
stats.set("mp", 40)

let spawn = tuple<int, int>(12, 24)
let playerSpawn = tuple<int, int>(64, 96)

Map<K, V>

Map<K, V> stores key/value pairs in an internal entry array. That keeps it safe for arbitrary Walrus values, but it also means lookups are currently linear rather than hashed. It is a good fit for small gameplay tables, settings, and metadata.

Creating a Map

Use mapOf<K, V>() to create an empty map:

Walrus
let stats = mapOf<string, int>()
let lootCounts = mapOf<string, int>()

Inserting and Updating Entries

Use set(key, value) to add a new entry or overwrite an existing one:

Walrus
let stats = mapOf<string, int>()
stats.set("hp", 120)
stats.set("mp", 40)
stats.set("hp", 150) # overwrite existing entry

Reading Values

Use getOr(key, fallback) when a key may not exist:

Walrus
let stats = mapOf<string, int>()
stats.set("hp", 120)

let hp = stats.getOr("hp", 0)
let armor = stats.getOr("armor", 0)

If "hp" exists, hp is 120. If "armor" does not exist, armor becomes the fallback 0.

Checking for Keys

Use has(key) when you need to know whether an entry exists before making a decision:

Walrus
if stats.has("hp"):
    print("hp exists")

Removing Entries

Use remove(key) to erase an entry. It returns true when the key existed and false when nothing was removed:

Walrus
let removed = stats.remove("mp")

Map Methods

MethodDescription
len() -> intReturns the number of entries.
isEmpty() -> boolReturns true when no entries exist.
has(key: K) -> boolReturns true if the key exists.
set(key: K, value: V)Inserts or overwrites a key.
getOr(key: K, fallback: V) -> VReturns the stored value or a fallback.
remove(key: K) -> boolRemoves a key and reports whether it existed.

Full Map Example

Walrus
let stats = mapOf<string, int>()
stats.set("hp", 120)
stats.set("mp", 40)

if stats.has("hp"):
    let hp = stats.getOr("hp", 0)
    print(hp)

stats.remove("mp")
print(stats.len())

Tuple<TFirst, TSecond>

Tuple<TFirst, TSecond> is a compact fixed-size pair with first and second fields.

Creating a Tuple

Use tuple(first, second) to create a pair:

Walrus
let spawn = tuple<int, int>(12, 24)
let namedPoint = tuple<string, int>("coins", 10)

Accessing Tuple Values

Read tuple members through first and second:

Walrus
let spawn = tuple<int, int>(12, 24)
let x = spawn.first
let y = spawn.second

Swapping a Tuple

Use swap() when you want the pair reversed:

Walrus
let pair = tuple<int, string>(7, "walrus")
let swapped = pair.swap()

# swapped.first is "walrus"
# swapped.second is 7

Tuple Fields and Methods

MemberDescription
firstThe first stored value.
secondThe second stored value.
swap()Returns a new tuple with the positions reversed.

Full Tuple Example

Walrus
let screen = tuple<int, int>(1920, 1080)
let flipped = screen.swap()

if screen.first == 1920 && screen.second == 1080:
    print(flipped.first)

Containers and Views

Walrus is moving toward a more data-oriented model, and container plus view<T> are part of that shift.

container

Use container when you want structured data that is meant to participate in Walrus' thread-aware, data-oriented pipeline:

Walrus
container velocity:
    int speed = 5
    int direction_x = 0

Conceptually, a container is for runtime data layout and batched processing, not just generic object behavior.

In the current first pass:

  • containers have named fields
  • fields can have defaults
  • containers can be constructed with the normal record-style syntax
  • containers are the only valid element type for view<T>

view<T>

view<T> represents lightweight access over container data:

Walrus
let world = [
    velocity { speed = 2, direction_x = 10 },
    velocity { speed = 4, direction_x = 20 }
]

let all = viewOf<velocity>(world)
let head = all.slice(0, 1)

Use it when you want to process batches of structured values rather than treat data as isolated objects.

Mutable iteration

Walrus
for mut v => all:
    v.direction_x += v.speed

Read-only iteration

Walrus
let ro = all.readonly()

for v => ro:
    print(v.speed)

Thread-safe direction

container and view<T> are part of Walrus' thread-safe design direction:

  • containers define structured runtime data
  • views make access windows explicit
  • readonly vs writable access is visible in the source

That is important groundwork for future real multi-threaded scheduling, even though the current first pass still uses a simple runtime path underneath.