Containers
Weave has one structured data type: the Container. A Container is an ordered sequence of values where any value can optionally have a symbol key — one structure that serves as a list, map, struct, etc. By keeping our structured data to a single interface, Weave guarantees that every structure is compatible with the operators.
Creating Containers
Container literals use square brackets:
# List-style — just values
numbers = [1, 2, 3]
words = ["hello", "world"]
# Map-style — key:value pairs
user = [name: "Alice", age: 30, role: "engineer"]
# Mixed — bare values and keyed values together
mixed = [1, "hello", active: true]
# Nested containers
team = [
name: "Backend",
scores: [95, 87, 92],
lead: [name: "Bob", level: "senior"]
]
# Empty container
empty = []There is no distinction between “creating a list” and “creating a map” — it is always a Container.
Accessing Data
By Index
Containers are 0-based and maintain insertion order. Negative indices count from the end. Integer indexing is always by position:
list = [10, 20, 30, 40]
list[0] # 10
list[-1] # 40
m = [a: 1, b: 3, c: 5]
m[0] # 1
m[2] # 5By Key
Access values by symbol or string key. Missing keys return null:
user = [name: "Alice", age: 30]
user[:name] # "Alice"
user["name"] # "Alice" — equivalent
user[:email] # nullDot-Access Syntax
c.foo is equivalent to c[:foo]. Works for any key without whitespace:
user = [name: "Alice", age: 30]
user.name # "Alice"
# Chaining on nested containers
config = [db: [host: "localhost", port: 5432]]
config.db.host # "localhost"
config[:db][:host] # "localhost" — equivalent
# Works with function pointers too
handlers = [greet: ^(name) { puts("Hello, {name}!") }]
handlers.greet("Alice") # Hello, Alice!
handlers[:greet]("Alice") # Hello, Alice! — equivalentMutating Containers
Assign by key, dot access, or index:
user = [name: "Alice", role: "dev"]
# By key
user[:role] = "lead"
# By dot access
user.role = "lead"
# By index
user[1] = "lead" # ... this works, but it's not idiomatic.Append values with <<:
items = [1, 2, 3]
items << 4 # [1, 2, 3, 4]Augmented assignment works as expected:
stats = [count: 0, total: 100]
stats.count += 1 # [count: 1, total: 100]You can freely mix — add keys to a keyless container, or append bare values to a keyed one:
list = [1, 2, 3]
list[:label] = "numbers" # [1, 2, 3, label: "numbers"]
record = [name: "Alice"]
record << 42 # [name: "Alice", 42]Encapsulated Data Objects
Weave is not an Object-Oriented language, but you can fake some aspects of objects using function pointers and closures:
# Fib container
fib = [ a: 0, b: 1 ] # initial state
next = ^(f) {
c = f[:a] + f[:b]
f[:a] = f[:b]
f[:b] = c
}
# Creates a closure over 'fib' and stores it within fib itself!
fib[:next] = partial(next, fib)
fib.next() # Returns the next Fib number with each call 1, 2, 3, 5, etc...Iteration
Iterating over a container yields values and ignores keys.
Map with *> applies a function to each value and preserves keys:
scores = [alice: 90, bob: 85, carol: 92]
scores *> ^(s) { s + 10 } # [alice: 100, bob: 95, carol: 102]Reduce with &> accumulates values into a single result:
fn sum(val, acc: 0) { acc + val }
scores &> sum # 267When you need keys, use the .keys property to iterate over them explicitly:
scores.keys *> ^(k) {
puts("{k}: {scores[k]}")
}Weave does not unpack key-value pairs during iteration. This keeps iteration simple: always values, always in insertion order.
Built-in Container Properties and Methods
Containers have a few special built-in properties and methods.
| Property | What it returns |
|---|---|
.keys |
Container of symbol keys |
.len |
Number of elements |
.sort() |
Sorts values (ascending) |
.sort(:desc) |
Values sorted descending |
.to_str() |
Values concatenated as a string |
data = [x: 10, y: 20, z: 5]
data.keys # [:x, :y, :z]
data.len # 3
data.sort # [z: 5, x: 10, y: 20]
data.sort(:desc) # [y: 20, x: 10, z: 5]
[1, 2, 3].to_str() # "123"
"hello".len # 5Slicing
[start:end] includes start and excludes end. Works on containers and strings:
list = [10, 20, 30, 40, 50]
list[1:3] # [20, 30]
str = "Hello"
str[0:3] # "Hel"Set Operations
| Operator | Name | Behavior |
|---|---|---|
+ |
Concatenation | Combines both, includes duplicates |
| |
Union | Combines both, no duplicates |
& |
Intersection | Elements in both |
!& |
Symmetric difference | Elements in one but not both |
- |
Difference | Elements in left but not right |
a = [1, 2, 3]
b = [2, 3, 4]
a + b # [1, 2, 3, 2, 3, 4]
a | b # [1, 2, 3, 4]
a & b # [2, 3]
a !& b # [1, 4]
a - b # [1]