Standard Library
FizzBee has no import statement. A curated set of modules and top-level
builtins is auto-imported globally and is always available. This list is
authoritative — anything not on it is not available.
Always in scope, no qualifier needed:
| Name | Purpose |
|---|---|
struct(field=val, …) |
Immutable, hashable. Anonymous struct (go.starlark.net starlarkstruct). Can be used in sets and as dict keys. Use when you want a tuple-with-named-fields that participates in hash-based collections. Cannot be mutated after construction. |
record(field=val, …) |
Mutable, NOT hashable. FizzBee record — ordered and printable in traces. Cannot be used in set or as a dict key (the hash-based collections will reject it). To put records in collections, use genericset / genericmap. |
enum(NAME1, NAME2, …) |
Enum factory. Color = enum("RED", "GREEN") then Color.RED. |
genericset() |
Set that supports non-hashable values (records, lists, dicts). Implemented via linear scan rather than hashing — minor performance cost, irrelevant at model-checker scale where collections hold a handful of entries. Use this when you need a set of records. |
genericmap() |
Map that supports non-hashable keys (records, lists, dicts). Same linear-scan implementation rationale as genericset. |
bag() |
Mutable multiset (counts duplicates). Methods: add, add_all, clear, discard, pop, remove. |
sum(iterable, start=0) |
Sum a list/set/bag/tuple of numbers. Returns int if all values are ints, float otherwise. Example: sum([a.balance for a in accounts]). |
symmetric_values(name, n) |
Legacy. Use the symmetry module instead (see below). |
Plus everything Starlark provides: len, range, min, max,
sorted, reversed, zip, enumerate, all, print, type, int, str,
bool, float, list, dict, set, tuple.
Note on any: any is a reserved keyword in FizzBee (deprecated alias
for oneof). The Python any([...]) predicate form is a parse error.
Use len([x for x in xs if cond]) > 0 instead.
Standard math functions. From go.starlark.net/lib/math:
math.sqrt(x) math.pow(x, y) math.exp(x) math.log(x)
math.log10(x) math.log2(x) math.fabs(x) math.mod(x, y)
math.ceil(x) math.floor(x) math.round(x) math.remainder(x, y)
math.sin(x) math.cos(x) math.tan(x)
math.asin(x) math.acos(x) math.atan(x) math.atan2(y, x)
math.sinh(x) math.cosh(x) math.tanh(x)
math.gamma(x) math.hypot(x, y)
Constants: math.pi, math.e, math.inf, math.nan.
Limited subset. Only the following five functions are available — all
other Python itertools names (accumulate, count, cycle, repeat,
groupby, islice, tee, takewhile, dropwhile, etc.) are not
implemented and will fail at parse time:
itertools.chain(*iterables)
itertools.combinations(iterable, r)
itertools.combinations_with_replacement(iterable, r)
itertools.permutations(iterable, r=None)
itertools.product(*iterables, repeat=1)
Use these only when the requirements actually call for “any subset of N
participants” or “any pair / any group of K”. Don’t reach for them by
default — most actions just pick one element with oneof.
Pattern A — fixed-size subset (assertions / per-pair checks). Use
combinations(items, k) inside a for loop. No oneof involved; you
iterate every k-sized subset deterministically:
# Raft "log matching" assertion: every pair of nodes must satisfy a property
always assertion LogMatching:
for pair in itertools.combinations(nodes, 2):
# ... check pair[0] and pair[1] ...
Pattern B — variable-size subset (nondeterminism in actions). Build
a list of subsets across allowed sizes, then oneof over that list. Pick
the size bounds deliberately:
# Helper — keep min_len and max_len near the call site so the choice is visible
all_subsets = []
for k in range(min_len, max_len + 1):
for s in itertools.combinations(items, k):
all_subsets.append(list(s))
chosen = oneof all_subsets
When picking min_len / max_len:
- Powerset minus empty →
min_len=1, max_len=len(items). For “split this expense among any non-empty subset of group members.” - Quorum / majority subsets →
min_len=N//2 + 1, max_len=N. For Raft-style “the leader needs votes from a majority of nodes”:all_subsets(eligible, NUM_NODES // 2 + 1, NUM_NODES). - Bounded committee → fixed window like
min_len=2, max_len=4. Useful when modeling has tight state-space limits and a true powerset (2^N) would blow up.
State-space cost is sum(C(N, k) for k in [min_len..max_len]). For
N=3 the full powerset is 7 branches; for N=5 it’s 31; for N=8 it’s 255.
Tighten the bounds before increasing N.
Domain factories for state-space reduction. See Symmetry Module API for full semantics:
symmetry.nominal(name, limit, materialize=False)
symmetry.ordinal(name, limit, reflection=False, materialize=False)
symmetry.interval(name, divergence=None, limit=None, start=0, reflection=False, materialize=False)
symmetry.rotational(name, limit, materialize=False, reflection=False)
Domain instances expose: fresh(), choices(), choose(), values(),
min(), max(), segments() (interval only).
Built-in methods on dict / set / bag / list / string follow Python naming.
Supported methods (registered in lib/starlark_types.go):
- dict:
clear,get,items,keys,pop,popitem,setdefault,update,values - set:
add,clear,difference,discard,intersection,issubset,issuperset,pop,remove,symmetric_difference,union - bag:
add,add_all,clear,discard,pop,remove - list / string: standard Starlark methods