Skip to the content.

Container

Virtual base class for all collection types. Defines the interface contract that List, Map, and future containers (Stack, Queue, etc.) must implement. Also defines the Iterator companion class for stateful cursor traversal.

Contents


Dependencies

. boop Container    # loaded automatically by List or Map

You typically don’t use Container directly — use List or Map instead.

Architecture

Container data lives in companion bash arrays, not in the pipe-delimited descriptor. Each instance owns a global array named __boop_data_${self}. Child constructors declare it as indexed (-ga for List) or associative (-gA for Map).

This avoids encoding arrays inside the descriptor string, which would be fragile and slow. Companion arrays give native bash performance for element access while the object system handles identity, dispatch, and lifecycle.

Virtual Methods

These crash with a descriptive error if a child class forgets to override:

Method Contract
get Retrieve element by key/index
set Store element at key/index
delete Remove element by key/index
length Return element count
clear Remove all elements
has Check if key/index exists (exit code)
toArray Serialize all elements to string
each Iterate with callback: callback key value

Provided Methods

These work for all containers via delegation to the virtual methods:

$container.isEmpty && printf "empty\n"    # delegates to length
$container.destroy                         # cleans up companion array + registry
into=s $container.toString                 # class, ID, type, length

Callback Iteration: each

Every container implements each, which calls a user function for every element. No subshells, no forks — the callback runs in the current shell and can read/write variables in the caller’s scope.

my_callback() { printf "[%s] = %s\n" "$1" "$2"; }

$list.each my_callback     # calls: my_callback 0 "alpha"
                            #        my_callback 1 "beta"

$map.each my_callback      # calls: my_callback "host" "localhost"
                            #        my_callback "port" "8080"

If the callback returns non-zero, iteration stops immediately (early exit).

Deep Traversal

Container provides methods for walking nested structures:

# itemAt — read through nested containers
into=val $matrix.itemAt 1 2            # matrix[1][2]
into=val $config.itemAt "db" "host"    # config["db"]["host"]

# setAt — write through nested containers
$matrix.setAt "99" 1 2                 # matrix[1][2] = "99"

When Container is loaded, it also augments boop with itemFrom and setOn, which start from a named property on any object:

into=val $obj.itemFrom "tags" 0        # obj.tags → List → get 0
$obj.setOn "tags" "urgent" 0           # obj.tags → List → set 0 "urgent"

Iterator: Stateful Cursor

Iterator is a companion class defined inside the Container source file. It inherits from boop (not Container) — it doesn’t hold data, it holds a reference to a container and a cursor position.

Lazy Delegation (Implicit Iterator)

Every Container instance has iterator methods that auto-create an internal Iterator on first use:

into=l List
$l.push "a" "b" "c"

while $l.hasNext; do
  into=val $l.next
  into=idx $l.iterIndex
  printf "[%s] %s\n" "$idx" "$val"
done
# [0] a
# [1] b
# [2] c

$l.iterReset                    # back to before-first
into=val $l.next                # "a" again

The internal Iterator is created lazily — if you never call $l.next or any other iterator delegation method, no Iterator object is created.

Delegation methods on Container:

Method Delegates to Description
next Iterator.next Advance cursor, return value
prev Iterator.prev Move cursor back, return value
hasNext Iterator.hasNext True if more elements ahead
hasPrev Iterator.hasPrev True if elements behind
current Iterator.current Value at current position
iterIndex Iterator.index Key/index at current position
iterReset Iterator.reset Reset cursor to before-first

Explicit Iterators (Independent Cursors)

For multiple independent cursors on the same container, create Iterator objects explicitly:

into=l List
$l.push "x" "y" "z"

into=iter1 $l.iterator
into=iter2 $l.iterator

into=v1 $iter1.next             # "x" — advances iter1 only
into=v2 $iter2.next             # "x" — iter2 is independent
into=v3 $iter1.next             # "y"
into=v4 $iter2.next             # "y"

Methods on Iterator objects:

Method Description
next Advance cursor and return value
prev Move cursor backward and return value
hasNext True if more elements ahead (exit code)
hasPrev True if elements behind (exit code)
current Value at current position (no movement)
index Key (Map) or integer index (List) at position
reset Reset cursor to before-first (position -1)

Position semantics: -1 = before first element (initial state), 0..length-1 = on an element.

Map Iterator Snapshots

For Map iterators, the ordered key list is snapshotted at creation time into a companion array (__boop_iterkeys_${iteratorID}). Mutations to the Map after the iterator is created don’t affect the snapshot. This is a deliberate trade-off — predictable traversal over live-view consistency.

into=m Map
$m.set "a" "1"
$m.set "b" "2"

into=iter $m.iterator
$m.set "c" "3"                  # added after iterator creation

__count=0
while $iter.hasNext; do
  $iter.next; (( __count++ ))
done
printf "%d\n" "$__count"        # 2 (not 3 — snapshot has a, b only)

Opting Out: noIterators

Subclasses that don’t want iterator support call $_Self.noIterators in their constructor. This replaces all iterator methods with stubs that crash with a clear message:

MyStack.new() {
  local _Class="${_Class:-MyStack}"
  local __MyStack_new_self
  into=__MyStack_new_self __boop.new "$@"
  declare -ga "__boop_data_${__MyStack_new_self}"
  $__MyStack_new_self.noIterators
  boop.pass "$__MyStack_new_self" ${into:-}
}

# Later:
$stack.next    # CRASH: "MyStack does not support iterators"

Inheritance Hierarchy

boop → Container → List  (indexed array)
                      → Map   (insertion-ordered associative array)
                      → Stack, Queue, etc. (future)

boop → Iterator  (companion to Container — stateful cursor)