Collections¶
Viper provides strongly-typed collections that mirror Python’s built-in collections while enforcing type safety.
Vector¶
ValueVector is compatible with Python’s list API:
>>> from dsviper import *
# Create vector<string>
>>> v = Value.create(TypeVector(Type.STRING), ["hello", "world"])
>>> v
['hello', 'world']
# Append
>>> v.append("!")
>>> v
['hello', 'world', '!']
# Extend with Python list
>>> v.extend(["from", "python"])
# Index access
>>> v[0]
'hello'
>>> v[-1]
'python'
# Slice
>>> v[1:3]
['world', '!']
# Length
>>> len(v)
5
# Iterate
>>> for item in v:
... print(item)
Type Safety¶
>>> v.append(42) # Wrong type
ViperError: expected type 'str', got 'int'
Set¶
ValueSet is compatible with Python’s set API:
# Create set<int64>
>>> s = Value.create(TypeSet(Type.INT64), {1, 2, 3, 2, 1})
>>> s
{1, 2, 3}
# Add
>>> s.add(4)
# Remove
>>> s.remove(1)
# Membership
>>> 2 in s
True
# Length
>>> len(s)
3
# Set operations
>>> s2 = Value.create(TypeSet(Type.INT64), {3, 4, 5})
>>> s.union(s2)
{2, 3, 4, 5}
>>> s.intersection(s2)
{3, 4}
Map¶
ValueMap is compatible with Python’s dict API:
# Create map<string, int64>
>>> m = Value.create(TypeMap(Type.STRING, Type.INT64), {"a": 1, "b": 2})
>>> m
{'a': 1, 'b': 2}
# Get/Set
>>> m["c"] = 3
>>> m["a"]
1
# Keys, values, items
>>> list(m.keys())
['a', 'b', 'c']
>>> list(m.values())
[1, 2, 3]
>>> list(m.items())
[('a', 1), ('b', 2), ('c', 3)]
# Delete
>>> del m["a"]
# Length
>>> len(m)
2
Complex Keys¶
Maps can use complex types as keys:
# map<tuple<int64, int64>, string>
>>> t = TypeMap(TypeTuple([Type.INT64, Type.INT64]), Type.STRING)
>>> m = Value.create(t, {(1, 2): "one-two", (3, 4): "three-four"})
>>> m[(1, 2)]
'one-two'
Optional¶
ValueOptional holds a value or nothing:
# Create empty optional<string>
>>> opt = Value.create(TypeOptional(Type.STRING))
>>> opt
nil
>>> opt.is_nil()
True
# Wrap a value
>>> opt.wrap("hello")
>>> opt
Optional('hello')
>>> opt.is_nil()
False
# Unwrap
>>> opt.unwrap()
'hello'
# Unwrap empty optional raises error
>>> empty = Value.create(TypeOptional(Type.STRING))
>>> empty.unwrap()
ViperError: Try to unwrap empty optional<string>
Initialize with Value¶
>>> opt = Value.create(TypeOptional(Type.INT64), 42)
>>> opt
Optional(42)
Tuple¶
ValueTuple holds heterogeneous values:
# tuple<string, int64, bool>
>>> t = TypeTuple([Type.STRING, Type.INT64, Type.BOOL])
>>> v = Value.create(t, ("hello", 42, True))
>>> v
('hello', 42, True)
# Access by index
>>> v[0]
'hello'
>>> v[1]
42
Variant¶
ValueVariant holds one value from a set of possible types:
# string | int64
>>> t = TypeVariant([Type.STRING, Type.INT64])
>>> v = Value.create(t, "a string")
>>> v.type()
string|int64
# Change the value
>>> v.wrap(42)
>>> v.unwrap()
42
# Wrong type
>>> v.wrap(3.14)
ViperError: expected type 'string|int64', got 'float'
XArray¶
Vector vs XArray: When to Use Each¶
Feature |
Vector |
XArray |
|---|---|---|
Indexing |
Integer indices (0, 1, 2…) |
UUID positions |
Insert/Remove |
Indices shift |
Positions stable |
Multiplayer editing |
Last-write-wins |
Merge-friendly |
Performance |
Faster for local use |
Slight overhead |
Use case |
Local arrays, batch processing |
Shared editable lists |
Why XArray? In multiplayer scenarios, two users might insert at “index 3” simultaneously. With Vector, one insert wins and the other is lost or corrupted. With XArray, each element has a UUID position that remains stable across merges.
Example: A shared todo list where multiple users add/remove items should use XArray. A local computation buffer should use Vector.
XArray Basics¶
ValueXArray preserves order during concurrent mutations using UUID positions instead of
indices:
>>> t = TypeXArray(Type.STRING)
>>> x = Value.create(t)
# Append returns the position
>>> pos1 = x.append("first")
>>> pos2 = x.append("second")
# Access by position (UUID)
>>> x[pos1]
'first'
# Insert at position
>>> x.insert(pos1, "inserted")
Any¶
TypeAny accepts any value:
>>> v = Value.create(TypeVector(Type.ANY))
>>> v.append("a string")
>>> v.append(42)
>>> v.append([1, 2, 3])
>>> v.description()
"['a string':string:any, 42:int64:any, [1, 2, 3]:vector<int64>:any]:vector<any>"
Nested Collections¶
Collections can be nested:
# vector<map<string, int64>>
>>> t = TypeVector(TypeMap(Type.STRING, Type.INT64))
>>> v = Value.create(t)
>>> v.append({"a": 1})
>>> v.append({"b": 2, "c": 3})
>>> v
[{'a': 1}, {'b': 2, 'c': 3}]
What’s Next¶
Structures and Enumerations - User-defined types
Database - Persisting collections