I'd like to extend a Clojure protocol to deal with Java primitive arrays.
(defprotocol PVectorisable
(to-vector [a]))
(extend-protocol PVectorisable
??????
(to-vector [coll]
(Vectorz/create ^doubles coll))
java.util.List
... other implementations......)
Is this possible, and if so what needs to go in the extend-protocol definition above (in place of "??????")?
The simplest solution is probably grabbing the class programatically with reflection.
(defprotocol do-a-thing
(print-thing [thing]))
(extend-protocol do-a-thing
(class (float-array 0))
(print-thing [_]
(println "it's a float array")))
Java's arrays go by some odd names. The float array, for example, is [F
. If you try to use that directly in the REPL, it'll choke on the unmatched [
. However, you can still use this name with, for example, Class/forName
.
(defprotocol do-another-thing
(print-another-thing [thing]))
(extend-protocol do-another-thing
(Class/forName "[F")
(print-another-thing [_]
(println "it's still a float array")))
This article goes into more detail about array classes.
As noted by hadronzoo, the Class/forName solution only works if it's the first definition of defprotocol, which limits the solution to only one array type per protocol (or having to make multiple defprotocol definitions). This can be made to work with multiple array types with a macro to avoid the problem of the reader not being able to parse the array symbols:
(defprotocol ISample
(sample [_]))
(defmacro extend-protocol-arrays []
(let [ba (symbol "[B")
ia (symbol "[I")
oa (symbol "[Ljava.lang.Object;")]
`(extend-protocol ISample
~ba
(sample [this#] (println "Byte Array"))
~ia
(sample [this#] (println "Int Array"))
~oa
(sample [this#] (println "Object Array")))))
(extend-protocol-arrays)
(sample (byte-array 0))
(sample (int-array 0))
(sample (make-array Object 0))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With