I want to switch on the class of a given object in order to encode it.
(defn encoded-msg-for [msg]
(case (class msg)
java.lang.Double (encode-double msg)
java.lang.String (encode-str msg)
java.lang.Long (encode-int msg)
java.lang.Boolean (encode-bool msg)
clojure.lang.PersistentArrayMap (encode-hash msg)
clojure.lang.PersistentVector (encode-vec msg)
nil "~"
)
)
When I call (encoded-msg-for {}), it returns No matching clause: class clojure.lang.PersistentArrayMap
What is odd is that putting the cases into a hash-map (with the classes as keys and strings as values) works perfectly well.
Also, (= (class {}) clojure.lang.PersistentArrayMap) is true. What comparison is happening here and how can I switch either on the class of the object itself or (better) something in its hierarchy?
I believe
casetreats the class names as literal symbols – it does not resolve them to actual classes:This is rather unintuitive, but so it works in Clojure’s
case. Anyway, the idiomatic way is to usedefmultianddefmethodinstead of switching ontype:The dispatcher uses the
isa?predicate which deals well with the comparisons of types, in particular it works well with Java inheritance.If you don’t want to use
defmulti, thencondpmight replacecasein your use case, as it properly evaluates the test-expressions. On the other hand it doesn’t provide constant time dispatch.