clj-commons / primitive-math

for the discerning arithmetician

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Interesting behavior when missing typehints

maxcountryman opened this issue · comments

While working with custom types I noticed some interesting and unexpected behavior when using primitive-math:

(deftype Foo [^short n])

(def foo (Foo. 42))

(p/inc (.n foo))

This results in the following exception:
IllegalArgumentException No matching method found: inc clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:80)

If we hint foo as ^Foo, i.e. (p/inc (.n ^Foo foo)) things work as expected. I'm not really sure why this is happening--I'm guessing that macro expansion is at least part of the issue?

@maxcountryman This isn't related to any macroexpansion, since using macroexpand first yields the same reflection warning.

This is because the Clojure compiler can't tell at compile-time what the type of foo is, nor its n field, so it doesn't know what inc to use. It seems like it should be obvious, but Lisp evaluation works a form at a time, and so there's no "connection" between defing foo and the form to retrieve n and increment it. (You might have evaluated something at the REPL redefining foo in-between those calls.)

Adding hints is one way around it, as you figured out. Another is to combine the whole thing under a let, so the compiler can guarantee that foo will be a Foo, like:

(let [foo (Foo. 42)]
  (p/inc (.n foo)))