Keep a nil nested in a collection used as a map key (jolt-zcm9) (#157)
canon-key canonicalizes a collection key by re-keying a native Janet
table by the canonical form of each element/entry. canon-key returns nil
for nil, and a Janet struct can't hold a nil key or value, so a nil set
member / nil map key / nil map value was dropped during canonicalization
— #{nil 1} canonicalized like #{1} and collided as a map key. So
(count {#{nil 1} :a, #{1} :b}) was 1 and (get {#{nil 1} :a} #{1}) was :a.
Box a nested nil before it goes into the table. The marker has to be
value-hashable, not the identity-hashed mutable-table sentinel the
transients use: the canonical struct becomes a long-lived phm key, and
its hash must survive the marshal/snapshot/fork that init-cached relies
on — an unmarshalled table gets a fresh address, so its hash isn't
preserved and the map can't find its own key. An interned keyword hashes
by content. Collision risk is only a real value equal to that exact
keyword, the same negligible class as canon-key's existing set/map struct
aliasing.
The transient sentinel stays a mutable table (it's built and consumed
within one op, never crossing a marshal boundary, so identity hashing is
stable there).
Co-authored-by: Yogthos <yogthos@gmail.com>
This commit is contained in:
parent
630eb2b9d3
commit
8192fc9541
2 changed files with 41 additions and 15 deletions
|
|
@ -72,6 +72,20 @@
|
|||
["frequencies of maps" "2" "(get (frequencies [{:a 1} (hash-map :a 1)]) {:a 1})"]
|
||||
["group-by collection key" "1" "(count (group-by identity [{:a 1} (hash-map :a 1)]))"])
|
||||
|
||||
# A nil element/key/value INSIDE a collection used as a map key must survive key
|
||||
# canonicalization — it used to be dropped, so the nil-containing collection
|
||||
# collided with the nil-free one (jolt-zcm9).
|
||||
(defspec "map / nil inside a collection key (jolt-zcm9)"
|
||||
["set key w/ nil distinct" "2" "(count {#{nil 1} :a, #{1} :b})"]
|
||||
["set key w/ nil neg lookup" "nil" "(get {#{nil 1} :a} #{1})"]
|
||||
["set key w/ nil pos lookup" ":a" "(get {#{nil 1} :a} #{nil 1})"]
|
||||
["set key just nil distinct" "false" "(= {#{nil} :x} {#{} :x})"]
|
||||
["map nil-value key distinct" "2" "(count {{:a nil} 1, {} 2})"]
|
||||
["map nil-value key neg" "nil" "(get {{:a nil} 1} {})"]
|
||||
["map nil-value key pos" "1" "(get {{:a nil} 1} {:a nil})"]
|
||||
["map nil-key key distinct" "2" "(count {{nil :a} 1, {} 2})"]
|
||||
["map nil-key key pos" "1" "(get {{nil :a} 1} {nil :a})"])
|
||||
|
||||
# Strictness: assoc bounds-checks vector indices; dissoc requires a map;
|
||||
# count rejects scalars; numerator/denominator have no ratio type.
|
||||
(defspec "map / strictness (throws like Clojure)"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue