Sets
A set holds items of one type, each only once, and has no order your
code can use. Each operation is called as set.<name>, and each one that
changes a set gives you a new one.
| Operation | Gives | Fails | SQL form |
|---|---|---|---|
count(items) |
Int, at least 0 |
jsonb_array_length(x)::bigint |
|
contains(items, item) |
Bool |
none | |
with(items, item) |
Set<T> with item; the same set if already there |
none | |
without(items, item) |
Set<T> without item |
none | |
union(a, b) |
Set<T>, items in either |
none | |
intersection(a, b) |
Set<T>, items in both |
none | |
difference(a, b) |
Set<T>, items in a and not in b |
none |
- No loop needed. Each of these is one operation in the runtime, so
you never walk a set with
forto get its result. - Named types. As for lists, an operation that gives
TorSet<T>keeps the items' named type. It doesn't keep a named set type, becausewithoutcan break that type'smin_length; build the result back into it by construction, as a named collection's lengths require (Named collection construction). forwalks a set in the canonical order of its values (Collections), the order everything that shows a set uses. The loop's variable takes the set's item type, as it does over a list. A set still has no index and no first item.- There is no
filter,any,allormapon a set. Usefor.
A bakery's labels, through every set operation and a for over a set:
type RecipeName = Text { min_length: 1 max_length: 40}type Hazard = Enum { gluten milk nuts sesame}type Recipe = Object { name: RecipeName warnings: Set<Hazard>}type SafeToEat = Bool {}/// Whether `recipe` carries none of the warnings in `avoid`.function safe_for(recipe: Recipe, avoid: Set<Hazard>) -> SafeToEat { return SafeToEat(set.count(set.intersection(recipe.warnings, avoid)) == 0)}/// Every warning a tray of recipes carries, each once.function tray_warnings(recipes: List<Recipe>) -> Set<Hazard> { var mut found: Set<Hazard> = [] for recipe in recipes { found = set.union(found, recipe.warnings) } return found}/// The warnings as a label prints them: a set is walked in its canonical/// order, which for an enum is the order its variants are declared in.function label(warnings: Set<Hazard>) -> List<Hazard> { var mut shown: List<Hazard> = [] for hazard in warnings { shown = list.append(shown, hazard) } return shown}
test "set operations give new sets" { var loaf: Recipe = Recipe { name: "Seeded loaf" warnings: [Hazard.sesame, Hazard.gluten] } var flapjack: Recipe = Recipe { name: "Flapjack" warnings: [Hazard.milk] } assert set.count(loaf.warnings) == 2 assert set.contains(loaf.warnings, Hazard.sesame) assert not set.contains(flapjack.warnings, Hazard.nuts) assert set.with(loaf.warnings, Hazard.gluten) == loaf.warnings // already there assert set.with(flapjack.warnings, Hazard.nuts) == [Hazard.milk, Hazard.nuts] assert set.without(loaf.warnings, Hazard.sesame) == [Hazard.gluten] var in_either: Set<Hazard> = set.union(loaf.warnings, flapjack.warnings) assert in_either == [Hazard.gluten, Hazard.milk, Hazard.sesame] assert set.intersection(loaf.warnings, [Hazard.sesame, Hazard.nuts]) == [Hazard.sesame] assert set.difference(loaf.warnings, [Hazard.sesame]) == [Hazard.gluten] assert safe_for(flapjack, [Hazard.nuts]) assert not safe_for(loaf, [Hazard.nuts, Hazard.sesame]) assert tray_warnings([loaf, flapjack]) == [Hazard.milk, Hazard.sesame, Hazard.gluten] assert label(loaf.warnings) == [Hazard.gluten, Hazard.sesame]}
Two sets are equal when they hold the same items, whatever the order, so
the literal on the right of == can be written in any order. Walking a
set is the only place its order shows, and it is always the canonical
order, on every target.
Markdown for agents: /docs/sets.md.Built from Jadpo main at a707efb.