Just ran into some interesting behavior with sorted-set
. Namely, the contract to the user for a normal set
is nil
when the element doesn't exist. However, with sorted-set
, the call will throw if the comparator doesn't gracefully handle the type. The reason makes sense (the comparator requires the ability to sanely compare elements), but the interface is somewhat confusing in the "no membership" case since we know that the element doesn't exist in the set if its type can't be compared by default.
In other words, I guess I'm proposing that a sane default behavior for a sorted-set using the default comparator should be nil
rather than throw
when the type cannot be compared, in order to allow nil
-punning.
As an example, this case was encountered when the form was passed to a keyword function (:foobar form)
, which allows nil-punning on a lot of other collections. Here's a small snippet:
`
user> (def normal #{"0000"})
;; => #'user/normal
user> (def sorted-version (sorted-set "0000"))
;; => #'user/sorted-version
user> normal
;; => #{"0000"}
user> sorted-version
;; => #{"0000"}
user> (:foobar normal)
;; => nil
user> (:foobar sorted-version)
Execution error (ClassCastException) at user/eval246355 (form-init59552761865518221.clj:8123).
class java.lang.String cannot be cast to class clojure.lang.Keyword (java.lang.String is in module java.base of loader 'bootstrap'; clojure.lang.Keyword is in unnamed module of loader 'app')
`
I think my options for handling this are either try/catch or write a custom comparator, and both seem a little wonky :)
I'm probably missing something, but thought it was interesting anyway :)