Dictionary
Insertion ordered key and value storage, key identity, mutation, and time and space complexity.
Dictionary is a built in, mutable mapping from scalar keys to values. Values may have different concrete runtime types. The native implementation uses a hash index with insertion order links.
Declaration and construction
Dictionary requires no import and no type arguments. Its constructor takes no arguments.
let record: Dictionary = Dictionary()
record.set(key: "name", value: "Aner")
record.set(key: "count", value: 2)
print(record.len())Output:
2Dictionary may be used in parameters, fields, return types, checked casts, and concrete arguments to user defined generic classes. Typed specializations are not supported. Dictionary is a reserved built in name.
A binding inferred as Dictionary retains that static type. Use an explicit Any binding when its value must later have a different concrete type. A let binding prevents rebinding and permits mutation of the referenced Dictionary.
Key and value contracts
Keys
Keys may be Bool, Int64, non NaN Float64, String, or null. Key identity includes the concrete runtime type: integer 1, float 1.0, Boolean true, and String "1" are four distinct keys.
Float64 -0.0 and 0.0 compare equal and have the same hash. Positive and negative infinity produced by arithmetic are valid keys. NaN is not a valid key because it does not have reflexive numeric equality. Collections, user objects, and native composite values are not valid keys. Key validation applies to lookup, membership, removal, and fallback lookup as well as insertion, including on an empty Dictionary.
There is no automatic key conversion, custom hash callback, or custom equality callback.
Values
A value may be an Int64, Float64, String, Bool, null, user object, native value, or nested collection. Unit () is not a valid value.
get and get_or return static type Any. type_of(value) identifies its concrete runtime type. cast<Type>(value) checks that type without conversion. For example, an Int64 cannot be cast to Float64; Float64(cast<Int64>(value)) performs an explicit conversion.
Presence and insertion order
contains distinguishes an absent key from a present key whose value is null. get requires the key to exist. get_or returns its fallback only when the key is absent; stored null is returned unchanged.
Updating an existing key replaces its value and preserves its insertion position. Removing an absent key is an operation with no effect. Removing a key and inserting it again moves it to the end.
All method arguments are evaluated before execution. A get_or fallback expression is evaluated even when its key exists.
Aliases, copies, and snapshots
Assignment shares the Dictionary reference. Dictionary equality is reference identity. An alias equals the original; copy() creates a different outer identity. A copy owns its copied scalar keys and values but retains references to nested collections and user objects. Native payloads retain their existing copy or alias behavior. Copying does not recursively clone nested graphs.
keys() and values() return separate Lists in insertion order. items() returns a List of two element Lists containing each key and value in that order. These results are snapshots, not live views: replacing a Dictionary entry does not replace the corresponding snapshot entry. Nested references remain shared. The List reference specifies operations on these result containers.
Methods
Methods accept positional arguments or named arguments with the labels below. Named arguments select destinations without changing expression evaluation order. A Unit result cannot be chained.
| Method | Result | Contract |
|---|---|---|
clear() | Unit | Remove all entries and release bucket storage. |
contains(key: Any) | Bool | Return whether the validated scalar key exists. |
copy() | Dictionary | Return a new shallow outer Dictionary in the same insertion order. |
get(key: Any) | Any | Return the value for an existing validated scalar key. |
get_or(key: Any, default: Any) | Any | Return the stored value or the fallback for an absent key. |
is_empty() | Bool | Return whether there are no entries. |
items() | List | Return insertion order key/value pairs as separate two element Lists. |
keys() | List | Return an insertion order snapshot of the keys. |
len() | Int64 | Return the live entry count. |
remove(key: Any) | Unit | Remove a validated scalar key if it exists. |
set(key: Any, value: Any) | Unit | Insert a new key or replace the value of an existing key. |
values() | List | Return an insertion order shallow snapshot of the values. |
Time and space complexity
Let n be the live entry count and b the allocated hash bucket count. Bounds assume bounded cost values and fixed length keys. Space below is additional auxiliary, result, or new persistent storage beyond the existing Dictionary and evaluated arguments.
Expected hash bounds require well distributed hashes. Hash lookup does not guarantee constant time; a collision chain can take O(n).
| Constructor or method | Time | Additional space |
|---|---|---|
Dictionary() | O(1) | O(1) empty container. |
clear() | O(n + b) | O(1) auxiliary; entries and buckets are released. |
contains(key: Any) | Expected O(1); worst O(n) | O(1). |
copy() | Expected O(n); worst O(n²) under collisions | O(n) new shallow Dictionary. |
get(key: Any) | Expected O(1); worst O(n) | O(1) result, plus copied payload bytes. |
get_or(key: Any, default: Any) | Expected O(1); worst O(n) | O(1) result, plus copied payload bytes. |
is_empty() | O(1) | O(1). |
items() | O(n) | O(n) outer List plus n separate two value Lists. |
keys() | O(n) | O(n) new List and copied keys. |
len() | O(1) | O(1). |
remove(key: Any) | Expected O(1); worst O(n) | O(1); the entry is released and bucket capacity is retained. |
set(key: Any, value: Any) | Expected amortized O(1); worst O(n), including rehash | O(1) new or replacement entry; worst O(n) transient bucket growth. |
values() | O(n) | O(n) new shallow List. |
Total Dictionary container storage is O(n + b), plus owned key and value payload bytes. A live pair occupies two value slots. The hash index and insertion order links share the stored key; maintaining insertion order does not require a duplicate key. Deletion releases an entry immediately, while buckets may retain earlier capacity. Deleted entries do not accumulate as tombstones. clear releases buckets.
Hashing a String key of k bytes costs O(k). String comparisons may inspect shared prefixes; collisions multiply that cost by the number of comparisons. Copying or storing an owned String of p bytes can add O(p) time and storage. Snapshot and copy methods include these payload costs. Native value copies retain their own payload costs. Nested references are shallow handles and are not recursively copied.
These bounds exclude parsing, argument evaluation, heap reference validation, automatic tracing, and display. The heap identity registry has expected constant time hash lookup; its collision cost depends on total heap size. With h outstanding heap objects, s traced slots, and g roots, tracing takes expected O(h + s + g) time and O(h) auxiliary space; hash collisions can increase the time.
Error behavior and resource limits
| Condition | Diagnostic |
|---|---|
Missing key passed to get | R2901 |
| Unsupported key or NaN key passed to a key operation | R2901 |
| Collection resource limit failure | R2901 |
| Host memory allocation failure in command line execution | E9003 |
| Wrong static argument type or arity; constructor arguments; typed specialization; Unit value | E1002 |
| Failed checked cast or unsupported Any equality operator | R1002 |
A native runtime failure clears the entire notebook Session. A parsing or type checking failure preserves previous Session state. Saved notebook outputs do not reconstruct live Dictionaries after a restart.
The execution heap permits 16,384 outstanding heap objects, 262,144 outstanding value slots, and 16 MiB of directly stored collection String bytes. These limits are shared with other heap backed values. Each Dictionary entry occupies two slots. Every owned key or value String copy contributes its stored byte count.
items() creates n pair containers and one outer container, with a total of 3n result slots. The result must fit the shared heap limits. Snapshot and copy methods also account for newly owned String copies. Nested references occupy slots without recursively copying referenced storage.
The limits are not a whole process RAM quota: native buffers, heap metadata, retained source, allocator overhead, and ordinary user class String fields are excluded. Native values retain their own operation limits.
Unreachable objects and cycles are reclaimed after a successful shared session cell. There is no collection during a cell, so repeated temporary allocations can reach the limits before completion. clear and remove release direct slot and String accounting; unreachable nested values are reclaimed at the next successful session collection.
Display is a bounded preview: at most 50 entries per collection, eight nested collection levels, and 8,192 output bytes. Strings are quoted and escaped. Cycles and omitted contents use markers such as <cycle:Dictionary>, <depth-limit:Dictionary>, and .... Display is not serialization; its state uses O(depth) active path storage and O(output bytes) result storage.
Examples
Missing and null values
let record = Dictionary()
record.set("name", "Aner")
record.set("optional", null)
print(record.contains("optional"))
print(record.get("optional"))
print(record.get_or(key: "missing", default: "fallback"))
print(record.get_or(key: "optional", default: "fallback"))Output:
true
null
fallback
nullTyped value access
let record = Dictionary()
record.set("count", 2)
record.set("label", "sample")
print(cast<Int64>(record.get("count")) + 1)
print(cast<String>(record.get("label")))Output:
3
sampleStrict key identity
let record = Dictionary()
record.set(1, "integer")
record.set(1.0, "float")
record.set(true, "Boolean")
record.set("1", "text")
print(record.len())
print(record.get(1))
print(record.get(1.0))Output:
4
integer
floatEntry order
let record = Dictionary()
record.set("first", 1)
record.set("second", 2)
record.set("first", 3)
print(record.keys())
record.remove("first")
record.set("first", 4)
print(record.keys())Output:
List("first", "second")
List("second", "first")In VS Code, the dictionary snippet provides an Aner example. Signature help describes parameters, result types, complexity, and scalar key constraints. The editor and native executable must support the same API.