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)))