IList and IMap
IList and IMap never change in place. Every operation returns a new collection that shares structure with the source, so they’re safe across threads.
Iteration and map/filter run a few times slower than list comprehensions. For hot numeric loops use the numeric lists.
from pyrula import IList
nums = IList([1, 2, 3, 4])
nums.map(lambda n: n * 2) # IList([2, 4, 6, 8])nums.filter(lambda n: n % 2 == 0) # IList([2, 4])nums.fold_left(0, lambda acc, n: acc + n) # 10nums.append(5) # new IList; nums is still length 4Heads and lookups return an Option:
nums.head() # Some(1)IList([]).head() # Nothingnums.find(lambda n: n > 2) # Some(3)An IList of Either splits without a loop:
results.collect_ok() # IList of the Ok valuesresults.collect_err() # IList of the Err valuesresults.partition_either() # (IList[ok], IList[err])from pyrula import IMap
m = IMap({"a": 1, "b": 2})
m["a"] # 1 (KeyError if missing)m.get("z", 0) # 0m.get_option("a") # Some(1)m.set("c", 3) # new IMap; m still has two keysm.remove("a") # new IMap without "a""b" in m # TrueIMap is a collections.abc.Mapping; IMap(x) takes a mapping or (key, value) pairs. Also map_values, filter_values, merge, to_dict.
Keys are matched by Python hash() and ==, as in a dict.
Iteration order
Section titled “Iteration order”IMap and ISet iterate in an order that depends only on their contents: equal collections iterate identically in any process and under any PYTHONHASHSEED. It is neither insertion nor sorted order. This holds for keys that are exact str, bytes, int, float, bool, None, or tuples and frozensets of those. Other key types (plain objects, Enum members, str subclasses with their own __hash__) can iterate differently between processes. Sort if the order matters.
Equality and hashing
Section titled “Equality and hashing”Collections and case classes (Ok, Err, Some, Success, Failure, Valid, Duration) compare by value like the nearest builtin.
IList,NonEmptyList,IntList,FloatListandBytesListact liketuple: equal only to the same class, soIList([1]) != [1].ISetandIntSetact likefrozenset:ISet([1]) == frozenset({1}) == IntSet([1]).IMapacts like a read-only mapping:IMap({"a": 1}) == {"a": 1}.
A value is hashable exactly when its elements are. Everything pickles and deep-copies, so values cross a ProcessPoolExecutor or par_map(..., result_codec="pickle").
Nesting depth
Section titled “Nesting depth”Hashing, comparing, ordering and formatting recurse in native code. A value nested more than 800 levels deep raises RecursionError instead of crashing, and sys.setrecursionlimit does not change that limit. Threads with small stacks (512 KiB for non-main threads on macOS) can overflow earlier, so keep values shallow there.