Generated from /std/set.deed and the module's own tests.
A set, written on top of std/hashmap.
A set is a map whose values carry nothing, and this is that map with the values hidden. Everything a set does that a map does not, union and intersection and difference, is a walk over one side asking the other whether it holds something, which is the operation the map is already fast at.
The shape is written out rather than aliased for the same reason
std/hashmap writes its own out: an alias over a list of lists does not
expand where it is used, so a signature naming it would say less than this
one does. What that costs is a type that is a mouthful; what it buys is that
a set is a map and a program can say so.
A set has no Empty. Every constructor takes a sample of the element type,
because an empty list takes its element type from where it is used and there
is nowhere here for it to take one from. That is the same shape
std/hashmap's empty has and for the same reason.
A set holding nothing, shaped by a sample of what it would hold.
fn none<T>(sample: T) -> List<List<Entry<T, Bool>>>
none
pure
pure
let s = none(0)
assert within(none("z"), big)
A set holding one item, which is how most of them start.
fn one<T>(item: T) -> List<List<Entry<T, Bool>>>
none
pure
pure
let s = including(including(one("a"), "b"), "a")
let s = including(including(one(1), 2), 3)
let left = including(including(one(1), 2), 3)
let right = including(including(one(3), 4), 5)
let small = including(one("a"), "b")
let big = including(including(one("a"), "b"), "c")
let s = including(one("a"), "b")
The same set with item in it, whether or not it was there before.
Not with, which is how a handler is installed and therefore a word the
grammar has already spoken for.
fn including<T>(subject: List<List<Entry<T, Bool>>>, item: T)
-> List<List<Entry<T, Bool>>>
none
pure
pure
let s = including(including(one("a"), "b"), "a")
let s = including(including(one(1), 2), 3)
let left = including(including(one(1), 2), 3)
let right = including(including(one(3), 4), 5)
let small = including(one("a"), "b")
let big = including(including(one("a"), "b"), "c")
let s = including(one("a"), "b")
Whether the set holds item.
fn has<T>(subject: List<List<Entry<T, Bool>>>, item: T) -> Bool
none
pure
pure
assert has(s, "a")
assert has(s, "b")
assert !has(s, "c")
assert !has(s, 0)
assert has(smaller, 1)
assert !has(smaller, 2)
assert has(smaller, 3)
assert has(intersection(left, right, 0), 3)
assert has(difference(left, right, 0), 1)
assert !has(difference(left, right, 0), 3)
How many items the set holds.
fn count<T>(subject: List<List<Entry<T, Bool>>>) -> Int
none
pure
pure
assert count(s) == 2
assert count(s) == 0
assert count(smaller) == 2
assert count(without(smaller, 9)) == 2
assert count(union(left, right)) == 5
assert count(intersection(left, right, 0)) == 1
assert count(difference(left, right, 0)) == 2
Every item, in the order the map gives them.
Not sorted: a set has no order of its own, and inventing one would need a comparison this cannot ask for.
fn items<T>(subject: List<List<Entry<T, Bool>>>) -> List<T>
none
pure
pure
assert length(items(s)) == 0
The same set without item, which is a rebuild rather than a removal.
std/hashmap has no way to take a key out, and the honest reason is that
nothing has needed one: a map is rebuilt from its entries here, which is
linear in the size of the set rather than in the size of one bucket. A
remove on the map would make this a bucket-sized walk, and that is the day
to write it.
fn without<T>(subject: List<List<Entry<T, Bool>>>, item: T) -> List<List<Entry<T, Bool>>>
none
pure
pure
let smaller = without(s, 2)
assert count(without(smaller, 9)) == 2
Everything in either set.
fn union<T>(left: List<List<Entry<T, Bool>>>, right: List<List<Entry<T, Bool>>>)
-> List<List<Entry<T, Bool>>>
none
pure
pure
assert count(union(left, right)) == 5
Everything in both sets.
fn intersection<T>(
left: List<List<Entry<T, Bool>>>,
right: List<List<Entry<T, Bool>>>,
sample: T,
)
-> List<List<Entry<T, Bool>>>
none
pure
pure
assert count(intersection(left, right, 0)) == 1
assert has(intersection(left, right, 0), 3)
Everything in the first set that is not in the second.
The sample is here for the same reason it is on none: the answer can be
empty, and an empty set has to be shaped by something.
fn difference<T>(
left: List<List<Entry<T, Bool>>>,
right: List<List<Entry<T, Bool>>>,
sample: T,
)
-> List<List<Entry<T, Bool>>>
none
pure
pure
assert count(difference(left, right, 0)) == 2
assert has(difference(left, right, 0), 1)
assert !has(difference(left, right, 0), 3)
Whether every item of the first set is in the second.
fn within<T>(left: List<List<Entry<T, Bool>>>, right: List<List<Entry<T, Bool>>>) -> Bool
none
pure
pure
assert within(small, big)
assert !within(big, small)
assert within(small, small)
assert within(none("z"), big)
How many entries the underlying map holds, which is what count reads.
Exposed because a set built by hand out of std/hashmap and one built here
are the same value, and a program mixing the two should be able to say so.
fn entries_of<T>(subject: List<List<Entry<T, Bool>>>) -> List<Entry<T, Bool>>
none
pure
pure
assert length(entries_of(s)) == 2