containers
specialized data structures
use containersThe collection of structures you summon when you need them:
Deque,PriorityQueue,SortedMap/SortedSet(B-tree),Trie(ART),RingBuffer, and which grows each release. It is the open roster (tier 1) that complements the closed set ofcollections(List/Map/Set, tier 0): each algorithm here is frozen (tier 0 content), but the roster changes (tier 1). It follows the template ofcollections: assocnew/with_order, generic, access viaat/get -> Optional(no overload of[]), colorless (the@mmtravels with the object, §5),Iterator, honest names (§14). The bar is what keeps the collection coherent as it accumulates, not a fixed list.
Construction convention (mirrors sort/sort_by)
Section titled “Construction convention (mirrors sort/sort_by)”T.new() gives natural order (the </== operator on the element, like collections.sort); T.with_order(cmp) gives a
custom comparator fn(K,K)->Order. A structure without a sensible natural order does not expose new(), instead of
inventing a third constructor: RingBuffer has only with_capacity (the capacity is the ring); PriorityQueue
offers both (natural when the element has <, custom via with_order).
SortedMap[int, V].new() // natural order of the keysSortedMap[Key, V].with_order(by_field) // custom comparatorPriorityQueue[int].new() // min-heap by natural orderPriorityQueue[Task].with_order(by_pri) // custom priority (Task has no natural order)RingBuffer[T].with_capacity(1024) // only with_capacity, no new()Deque[T]: double-ended queue
Section titled “Deque[T]: double-ended queue”Contiguous ring: push/pop O(1) at both ends. It needs no order, being a sequence, not keyed.
decl Deque[T] { ... }fn (Deque[T]) new() -> Deque[T]fn (Deque[T]) with_capacity(n: usize) -> Deque[T]
fn (d: *Deque[T]) push_front(item: T)fn (d: *Deque[T]) push_back(item: T)fn (d: *Deque[T]) pop_front() -> Optional[T] // none if emptyfn (d: *Deque[T]) pop_back() -> Optional[T]fn (d: Deque[T]) front() -> Optional[T]fn (d: Deque[T]) back() -> Optional[T]fn (d: Deque[T]) get(i: usize) -> Optional[T] // logical index (0 = front)fn (d: Deque[T]) len() -> usizefn (d: Deque[T]) is_empty() -> boolfn (d: *Deque[T]) clear()fn (d: Deque[T]) iter() -> Iterator[T] // front → backPriorityQueue[T]: binary heap
Section titled “PriorityQueue[T]: binary heap”new() is a min-heap by natural order; with_order(cmp) is custom priority (max-heap = inverted comparator).
decl PriorityQueue[T] { ... }fn (PriorityQueue[T]) new() -> PriorityQueue[T] // min-heap ('<' on T)fn (PriorityQueue[T]) with_order(cmp: fn(T, T) -> Order) -> PriorityQueue[T]fn (PriorityQueue[T]) with_capacity(n: usize) -> PriorityQueue[T] // natural order + pre-reserve
fn (q: *PriorityQueue[T]) push(item: T)fn (q: *PriorityQueue[T]) pop() -> Optional[T] // removes and returns the highest-priority onefn (q: PriorityQueue[T]) peek() -> Optional[T] // peeks without removingfn (q: PriorityQueue[T]) len() -> usizefn (q: PriorityQueue[T]) is_empty() -> booluse containers
pq := containers.PriorityQueue[Task].with_order(by_priority) // Task has no natural order → with_orderpq.push(task_a)pq.push(task_b)next := pq.pop() // Optional[Task]: the highest-priority oneSortedMap[K, V] / SortedSet[T]: B-tree
Section titled “SortedMap[K, V] / SortedSet[T]: B-tree”The differential over the hash Map/Set is the maintained order: ordered iteration, range, floor/ceiling.
O(log n) operations.
decl SortedMap[K, V] { ... }fn (SortedMap[K, V]) new() -> SortedMap[K, V] // natural order of Kfn (SortedMap[K, V]) with_order(cmp: fn(K, K) -> Order) -> SortedMap[K, V]
fn (m: *SortedMap[K, V]) put(key: K, value: V) -> Optional[V] // old value, if there was onefn (m: SortedMap[K, V]) get(key: K) -> Optional[V]fn (m: *SortedMap[K, V]) remove(key: K) -> Optional[V]fn (m: SortedMap[K, V]) contains(key: K) -> boolfn (m: SortedMap[K, V]) len() -> usizefn (m: SortedMap[K, V]) first() -> Optional[(K, V)] // smallest keyfn (m: SortedMap[K, V]) last() -> Optional[(K, V)] // largest keyfn (m: SortedMap[K, V]) floor(key: K) -> Optional[(K, V)] // largest key ≤ keyfn (m: SortedMap[K, V]) ceiling(key: K) -> Optional[(K, V)] // smallest key ≥ keyfn (m: SortedMap[K, V]) range(lo: K, hi: K) -> Iterator[(K, V)] // [lo, hi) in orderfn (m: SortedMap[K, V]) iter() -> Iterator[(K, V)] // everything, in key orderfn (m: SortedMap[K, V]) keys() -> Iterator[K]fn (m: SortedMap[K, V]) values() -> Iterator[V]
decl SortedSet[T] { ... }fn (SortedSet[T]) new() -> SortedSet[T]fn (SortedSet[T]) with_order(cmp: fn(T, T) -> Order) -> SortedSet[T]fn (s: *SortedSet[T]) add(item: T) -> bool // false if it already existedfn (s: *SortedSet[T]) remove(item: T) -> boolfn (s: SortedSet[T]) contains(item: T) -> boolfn (s: SortedSet[T]) len() -> usizefn (s: SortedSet[T]) first() -> Optional[T]fn (s: SortedSet[T]) last() -> Optional[T]fn (s: SortedSet[T]) floor(item: T) -> Optional[T]fn (s: SortedSet[T]) ceiling(item: T) -> Optional[T]fn (s: SortedSet[T]) range(lo: T, hi: T) -> Iterator[T] // [lo, hi) in orderfn (s: SortedSet[T]) iter() -> Iterator[T] // in orderscores := containers.SortedMap[int, string].new()scores.put(90, "ana"); scores.put(75, "beto"); scores.put(60, "caio")loop pair in scores.range(70, 100) { ... } // [70,100) in order: (75,"beto"), (90,"ana")Trie[K, V]: prefix tree, generic over a sequence (ART)
Section titled “Trie[K, V]: prefix tree, generic over a sequence (ART)”The key is a sequence []K; string (K = byte) is the special case. The implementation is an ART (adaptive
radix tree) for byte keys (the common case), with general nodes for other K. The differential over the Map is the
prefix operations.
decl Trie[K, V] { ... }fn (Trie[K, V]) new() -> Trie[K, V]
fn (t: *Trie[K, V]) put(key: []K, value: V) -> Optional[V]fn (t: Trie[K, V]) get(key: []K) -> Optional[V]fn (t: *Trie[K, V]) remove(key: []K) -> Optional[V]fn (t: Trie[K, V]) contains(key: []K) -> boolfn (t: Trie[K, V]) len() -> usizefn (t: Trie[K, V]) with_prefix(prefix: []K) -> Iterator[([]K, V)] // pairs whose key starts with prefixfn (t: Trie[K, V]) longest_prefix(key: []K) -> Optional[([]K, V)] // the longest prefix of key presentfn (t: Trie[K, V]) iter() -> Iterator[([]K, V)] // in lexicographic order
alias StringTrie[V] = Trie[byte, V] // the special case: string key (the UTF-8 bytes, s.bytes, §16)routes := containers.Trie[byte, Route].new()routes.put("/api/users".bytes, users_route) // key = UTF-8 bytes of the string (§16)match routes.longest_prefix("/api/users/42".bytes) { // the longest prefix present none => not_found() hit => dispatch(hit)}RingBuffer[T]: fixed-capacity ring (two ports)
Section titled “RingBuffer[T]: fixed-capacity ring (two ports)”Fixed capacity (does not grow): the capacity defines the ring, so there is only with_capacity. The overflow
policy is yours, at the call site, via two ports: push rejects when full; force_push overwrites the
oldest one.
decl RingBuffer[T] { ... }fn (RingBuffer[T]) with_capacity(n: usize) -> RingBuffer[T] // mandatory (defines the ring), no new()
fn (r: *RingBuffer[T]) push(item: T) -> bool // PORT 1: false if full (rejects)fn (r: *RingBuffer[T]) force_push(item: T) -> Optional[T] // PORT 2: overwrites; returns the evicted one (none if there was space)fn (r: *RingBuffer[T]) pop() -> Optional[T] // removes the oldest (FIFO)fn (r: RingBuffer[T]) front() -> Optional[T] // the oldestfn (r: RingBuffer[T]) back() -> Optional[T] // the newestfn (r: RingBuffer[T]) get(i: usize) -> Optional[T] // logical index (0 = oldest)fn (r: RingBuffer[T]) len() -> usizefn (r: RingBuffer[T]) capacity() -> usizefn (r: RingBuffer[T]) is_full() -> boolfn (r: RingBuffer[T]) is_empty() -> boolfn (r: RingBuffer[T]) iter() -> Iterator[T] // oldest → newestbuf := containers.RingBuffer[Event].with_capacity(1024)if !buf.push(ev) { dropped.inc() } // port 1: rejects if fullevicted := buf.force_push(ev) // port 2: overwrites; Optional of the evicted oneCuration
Section titled “Curation”- Open roster, tier 1.
collectionsis the closed and fundamental set (List/Map/Set, tier 0);containersis the collection that grows. Each algorithm inside is frozen (tier 0 content); the roster changes (tier 1), and that is what makes it an official lib and not core. - The
collectionstemplate governs, not a fixed list: assocnew/with_order, generic,at/get -> Optional(no overload of[]), colorless,Iterator, honest names. The bar is what keeps the collection from turning into a Frankenstein as it accumulates structures. new()+with_order()mirroringsort/sort_by: natural by default (<on the element), custom viafn(K,K)->Order. A structure without a natural order does not invent a third constructor:RingBufferhas onlywith_capacity;PriorityQueueaccepts both (gated on<).- RingBuffer is fixed-capacity, two ports.
pushrejects (returns whether it fit),force_pushoverwrites (returns the evicted one). The overflow policy is the user’s, at the call site, not fixed in the type. - Trie generic over a sequence (
[]K;string/K=byteis the special case; ART impl for the bytes). The prefix operations (with_prefix/longest_prefix) are what justify theTrieagainst theMap. - SortedMap/SortedSet are B-tree. Order plus
rangeplusfloor/ceilingare the differential over the hashMap/Set; iteration always in key order. - No allocator in the signatures (colorless): everything allocates via the context’s
@mm;range/iter/with_prefixare lazyIterator. - The collection grows. Next candidates, under the same template: intrusive list (the most coupled to the
language, with the node embedding the link,
*T), union-find (disjoint-set), bloom filter, LRU cache, skip list. They come in as additions (open roster), without touching what already exists.