I have some Clojure code that is trying to interop through a couple layers of Java code (in this case, java.nio.Path
by way of java.nio.file.WatchEvent<?>
:
(defn unroll-event
[^WatchEvent event]
{ :kind (.kind event)
:context (.context event)
:path (-> event .context .toAbsolutePath .toString)})
In this code, I have type hinted event
, so I would think it should be able to figure out what .context
is supposed to return, and as such, be able to figure out what .toAbsolutePath
and .toString
do. I think in this case, since .context
is defined has returning a generic type T
, I am wondering if I can type hint the call to .context
. I've tried just prepending ^java.nio.file.Path
to .context
, and ^Path
and ^String
to .toAbsolutePath
and toString
, respectively, but I still get the warnings:
Reflection warning, junkcode/core.clj:28 - reference to field toAbsolutePath can't be resolved.
Reflection warning, junkcode/core.clj:28 - reference to field toString can't be resolved.
Is there something I can do in this case? Is it because ->
is a macro and there are special rules for type hinting within that?
(-> x .blah ^String .bar)
expands to, basically, (^String .bar (.blah x))
, which is clearly not where you want the hint. The point is that type-hinting does not have special behavior in any context (eg, macros): it's just metadata applied to the source-code symbols. In your example ->
, there is no place that you can put metadata on the input form that will cause it to be where you want in the output form. So you need to write some other form, like (-> ^Path (.context event) .toAbsolutePath str)
, for example.
Also, Clojure's inferencer doesn't know anything about generic types, so a method-returning-T gets treated as a method-returning-Object, which explains why you need to hint at all in here.
I don't know if this has always been the case, but in Clojure 1.4.0, 1.5.1, and 1.6.0, you can type-hint at any point in ->
as long as you use parentheses:
user=> (set! *warn-on-reflection* true)
true
user=> (fn [^java.nio.file.WatchEvent e]
(-> e ^java.nio.file.Path .context .toAbsolutePath))
Reflection warning, /private/var/folders/9_/wdph6m796zzc8trzcbtcmhrn5bjpt0/T/form-init8364673644863044068.clj:1:35 - reference to field toAbsolutePath on java.lang.Object can't be resolved.
#<user$eval1995$fn__1996 user$eval1995$fn__1996@8128f39>
user=> ; but no warning if we do
user=> (fn [^java.nio.file.WatchEvent e]
(-> e ^java.nio.file.Path (.context) .toAbsolutePath))
#<user$eval1999$fn__2000 user$eval1999$fn__2000@4747e32a>
The only difference is the parens around .context
.
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