Here's a question, in Clojure there are our typical HashMaps which allow assoc'ing any kind of keys on them, and then we have some more specialized kinds of maps for specific keys. Most of them are however also open to allow assoc'ing keys outside their usual range, gracefully reverting to hashmaps when the operation would break the data structure's invariants. Examples below:
Records are open to assoc, and revert to hashmaps when dissoc'ing a record field
(defrecord Person [name lang])
;; => clojure.data.int_map_test.Person
(let [p (->Person "john" "en_CA")]
[p (assoc p :foo :bar) (dissoc p :lang)])
;; =>
;; [#clojure.data.int_map_test.Person{:name "john", :lang "en_CA"}
;; #clojure.data.int_map_test.Person{:name "john", :lang "en_CA", :foo :bar}
;; {:name "john"}]
On the other hand, struct-maps are open to assoc, but closed to dissoc
(let [sm (struct-map (create-struct :name :lang) :name "John" :lang "en_CA")]
[(assoc sm :foo "bar")
(try (dissoc sm :lang) (catch Exception e (.toString e)))])
=> [{:name "John", :lang "en_CA", :foo "bar"}
"java.lang.RuntimeException: Can't remove struct key"]
An other example is ArrayMaps which eventually turn into HashMaps as they grow. That's not exactly the same, there's no specific key outside the map's range, but similar in a more general sense of, when an operation either violates invariants or performance expectations, fall back to the general HashMap.
That makes me wonder, how do people feel about int-map/int-set which instead throw exception if you try and add a non-integer key?
(assoc (i/int-map 1 2) :a "b")
ClassCastException class clojure.lang.Keyword cannot be cast to class java.lang.Number
Should it be modified to fall back to a hashmap instead? It seems like that is more in line with Clojure philosophy of open data structures.
It could be implemented similar to PersistentStructMap
, which holds an extra map in which it stores extra key/value pairs which are not part of the struct's definition:
final IPersistentMap ext;
The same thing could be done in PersistentIntMap
(deftype PersistentIntMap
[^INode root
^long epoch
meta
^IPersistentMap ext]
;; ...
What do you think?
Also, the same question but for int-sets