Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Protocols by

It is possible to extend protocols to primitive arrays but specifying the class for the type is a little tricky:

(defprotocol P (p [_])) (extend-protocol P (Class/forName "[B") (p [_] "bytes")) (p (byte-array 0)) ;; => "bytes"

However, things go bad if you try to do more than one of these:

`
(extend-protocol P
(Class/forName "[B") (p [_] "bytes")
(Class/forName "[I") (p [_] "ints"))
CompilerException java.lang.UnsupportedOperationException: nth not supported on this type: Character, compiling:(NO_SOURCE_PATH:1:1)

clojure.lang.Compiler.analyze (Compiler.java:6380)
clojure.lang.Compiler.analyze (Compiler.java:6322)
clojure.lang.Compiler$MapExpr.parse (Compiler.java:2879)
clojure.lang.Compiler.analyze (Compiler.java:6369)
clojure.lang.Compiler.analyze (Compiler.java:6322)
clojure.lang.Compiler$InvokeExpr.parse (Compiler.java:3624)
clojure.lang.Compiler.analyzeSeq (Compiler.java:6562)
clojure.lang.Compiler.analyze (Compiler.java:6361)
clojure.lang.Compiler.analyze (Compiler.java:6322)
clojure.lang.Compiler$BodyExpr$Parser.parse (Compiler.java:5708)
clojure.lang.Compiler$FnMethod.parse (Compiler.java:5139)
clojure.lang.Compiler$FnExpr.parse (Compiler.java:3751)

Caused by:
UnsupportedOperationException nth not supported on this type: Character

clojure.lang.RT.nthFrom (RT.java:857)
clojure.lang.RT.nth (RT.java:807)
clojure.core/emit-hinted-impl/hint--5951/fn--5953 (core_deftype.clj:758)
clojure.core/map/fn--4207 (core.clj:2487)
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:60)
clojure.lang.RT.seq (RT.java:484)
clojure.lang.RT.countFrom (RT.java:537)
clojure.lang.RT.count (RT.java:530)
clojure.lang.Cons.count (Cons.java:49)
clojure.lang.Compiler.analyze (Compiler.java:6352)
clojure.lang.Compiler.analyze (Compiler.java:6322)

`

The code in {parse-impls} is seeing the second {(Class/forName "[I")} as a function, not as a new type. One workaround for this is to only extend the protocol to one type at a time.

It would be even better (moving into enhancement area) if there was a syntax here to specify primitive array types - we already have the syntax of {bytes, ints, longs}, etc for type hints and those seem perfectly good to me.

5 Answers

0 votes
by

Comment made by: nahuel

It also breaks when extending only one array type:

`
(extend-protocol P
String (p [_] "string")
(Class/forName "[B") (p [_] "ints")
)

;=> CompilerException java.lang.UnsupportedOperationException: nth not supported on this type ...
`

But changing the declaration order fixes it:

`
(extend-protocol P
(Class/forName "[B") (p [_] "ints")
String (p [_] "string")
)

;=> OK
`

0 votes
by

Comment made by: alexmiller

Dupe of CLJ-1790

0 votes
by

Comment made by: alexmiller

On further inspection, I don't think this is a dupe of CLJ-1790 but merely a related problem.

0 votes
by

Comment made by: glchapman

Using Class/forName has a further problem, as type-hints on the {{this}} parameter are longer emitted:

user=> (extend-protocol P (Class/forName "[B") (p [this] (aget this 0))) Reflection warning, NO_SOURCE_PATH:2:50 - call to static method aget on clojure.lang.RT can't be resolved (argument types: unknown, int).

Reflection warnings are also generated for non-primitive arrays (so just supporting {{ints}} etc, won't completely fix this problem). It would be good to have a solution which covered all the problems with extending protocols to arrays.

0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJ-1381 (reported by alexmiller)
...