Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tuples, Vecs, and Dicts

Whim arrays are values. Tuples are fixed and immutable. Vecs and dicts are mutable.

Tuples

A tuple may hold a different type at each position:

$user = (7, 'Ada', true);
assert!($user[0] == 7);
assert!($user is (int, string, bool));

A one-item tuple needs a trailing comma:

$one = ('only',);
assert!($one[0] == 'only');

() is not a value. Tuples may hold 1 through 12 items. Their indexes do not change, and tuple elements reject writes.

A tuple type may end with a rest type:

function read_row((int, ...string) $row): int {
  return $row[0];
}

assert!(read_row((7, 'a', 'b')) == 7);

Vecs

A vec has dense uint keys from zero:

$names = vec['Ada', 'Grace'];
$names[1] = 'Hopper';
$names[] = 'Linus';

assert!($names == vec['Ada', 'Hopper', 'Linus']);

Indexed assignment must replace an existing position. Use $vec[] = $value to append.

The fill form evaluates its value once, then repeats it:

$zeros = vec[0; 4];
assert!($zeros == vec[0, 0, 0, 0]);

Spread a vec or tuple into a vec literal:

$middle = vec[2, 3];
assert!(vec[1, ...$middle, 4] == vec[1, 2, 3, 4]);

A dict cannot spread into a vec.

Dicts

A dict accepts bool, int, uint, string, and fresh keys. It keeps insertion order:

$scores = dict['Ada' => 10, 'Grace' => 12];
$scores['Ada'] = 11;
$scores['Linus'] = 9;

assert!($scores['Ada'] == 11);
assert!(length!($scores) == 3u);

Keys do not convert. 1, 1u, '1', and true are four different keys. Copies of a fresh value name the same entry. Its uint cast names a separate entry. The unsigned maximum is a valid key. Vec, tuple, and string positions accept either int or uint. Both 1 and 1u name position one. Negative positions and positions outside the value’s length are out of bounds. Collection lengths and iteration keys for vecs and tuples use uint.

Replacing a value keeps the key’s place. Removing a key and adding it again moves it to the end.

A duplicate literal or spread key keeps its first place and its last value:

$key = 'a';
$values = dict[$key => 1, 'b' => 2, $key => 3];
assert!($values == dict['a' => 3, 'b' => 2]);

A dict spread accepts a tuple, vec, or dict. A tuple or vec contributes uint keys. A dict keeps its keys.

Reading entries

An ordinary indexed read requires an existing index. Reading a missing vec position, tuple position, or dict key throws OutOfBoundsError. Use ?? to provide a fallback for a missing or null entry:

$values = dict['ready' => true];
assert!(!contains_key!($values, 'missing'));
assert!(($values['missing'] ?? false) == false);

contains_key! checks existence, including entries whose value is null. contains! checks values:

$values = vec[10, 20];
assert!(contains_key!($values, 1));
assert!(contains_key!($values, 1u));
assert!(contains!($values, 20));

length! works on every array and on strings and returns uint.

Removing entries

remove!($array, $key) removes and returns one entry. On a vec, it preserves order by shifting every later item left. remove_first!($vec) is the same ordered operation at index zero. Use these forms when remaining indexes and iteration order must not change.

Both operations take time in proportion to the items after the removed one. Repeated remove_first! calls therefore take quadratic time. Use Whim\DataStructure\Deque for repeated FIFO removal.

swap_remove!($vec, $index) instead moves the last item into $index. It takes constant time on an unshared vec, but changes order. Use it for unordered work sets and pools. remove_last!($vec) also takes constant time and does not reorder the remaining items.

$values = vec['a', 'b', 'c', 'd'];
$removed = swap_remove!($values, 1);

assert!($removed == 'b');
assert!($values == vec['a', 'd', 'c']);

All vec mutations may first copy storage when another vec shares it.

Iteration

foreach walks keys and values in array order:

$seen = vec[];
foreach (dict['a' => 1, 'b' => 2] as $key => $value) {
  $seen[] = $key . $value;
}

assert!($seen == vec['a1', 'b2']);

The loop binds copies. Assigning $key or $value does not change the array. The loop also keeps the set of entries with which it began, so changing the source during the loop does not change the current walk.