Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you type hint within the threading (->) macro?

Tags:

macros

clojure

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?

like image 934
Nick Klauer Avatar asked Oct 07 '12 01:10

Nick Klauer


2 Answers

(-> 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.

like image 68
amalloy Avatar answered Sep 22 '22 11:09

amalloy


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.

like image 25
duelin markers Avatar answered Sep 20 '22 11:09

duelin markers