Lists
(1, 2, 3)
("Truth", "Dare")
()
("Only entry",)
The trailing comma is required for a one-entry list. Parentheses without the comma are grouping.
Every value type can be used as a list entry type, including scalar and nested types:
let scores: List<number> = (10, 20, 30);
let names: List<str> = ("Mina", "Noor");
let flags: List<bool> = (true, false);
let groups: List<List<number>> = ((1, 2), (3, 4));
Add when to include an entry conditionally:
let rewards: List<str> = ("base", "bonus" when score >= 100);
Use + to concatenate lists and - to remove matching entries. Both return new lists and preserve the remaining order:
let all = common + rare;
let safe = all - blocked;
Ranges
Ranges create consecutive whole-number lists:
1..5
1..=5
1..5 contains (1, 2, 3, 4). The .. form leaves out its end. 1..=5 includes 5.
Use a range anywhere a list is accepted:
Rand::pick(1..=20)
3 in 1..5
List indexes begin at 1:
values[1]
values[position]
An index outside the list resolves to None. An invalid non-positive or non-whole index produces an error.
Mutating list methods
list.push(value)
list.pop()
list.insert(index, value)
list.remove(index)
list.clear()
These methods require an assignable mutable list. push() appends a value. insert() accepts a one-based index from 1 through list.len() + 1. pop() removes the last entry, remove() removes an existing one-based index, and clear() removes everything.
pop() and remove() return the removed entry. Empty pop() returns None. push(), insert(), and clear() return None. Invalid indexes produce an error.
Lists use value semantics: mutating one list does not mutate another list from which it was copied.
List methods and closures
list.len()
list.contains(value)
list.first()
list.last()
list.filter(|entry| condition)
list.find(|entry| condition)
list.map(|entry| value)
list.any(|entry| condition)
list.all(|entry| condition)
list.take(amount)
list.take(minimum..=maximum)
contains() returns a boolean using Sugar value equality. first() and last() return the corresponding entry or None when empty. filter() and map() return new lists. find() returns the first match or None. any() and all() return booleans. take() keeps entries from the beginning; its ranged form also requires the source to contain at least the range’s minimum.
The |entry| syntax creates an Fn closure. Closures are first-class expressions: they can also be assigned, passed, stored in List<Fn>, and called. Closure bodies may read surrounding values but cannot perform outside effects.
The same methods work on every Sugar list, including SEATS:
SEATS.len()
SEATS.filter(|seat| seat != SELF)
SEATS.find(|seat| seat.joi.id == ACTOR.id)
SEATS.map(|seat| seat.joi.name)
SEATS.any(|seat| seat.health <= 0)
SEATS.all(|seat| seat.ready)
SEATS.take(2)
SEATS.take(1..=3)
Membership
"Dare" in modes
"Skip" not in modes
The value on the right must be a list.
Objects
{
name: "Challenge",
reward: 20,
stats: {
difficulty: 15,
attempts: 3
}
}
Object property names follow Sugar naming rules. Read them with dot or dynamic access:
challenge.reward
challenge[chosenProperty]
len(object) returns its number of own properties.
Nested assignment
When the variable can be changed:
challenge.stats.attempts++;
challenge[chosenProperty] = nextValue;
Item variables and other objects provided by JOI cannot be changed with an assignment.
Safe values only
Collections may contain every Sugar value type. Fn values may only live in execution-local values; persisted collections cannot contain them. Sugar blocks prototype access and the object keys __proto__, prototype, and constructor.
Limits:
- Maximum nesting depth: 5 levels
- Maximum list entries: 100
- Maximum object properties: 100
- Maximum serialized collection size: 16,000 characters
These limits apply to collections held by let variables, Memory variables, and App variables.