How to type hint eval'd `fn`-calls

How do I eval a fn declaration and keep type hints? I can’t get it to work. The expected result is that (eval generated-fn2) won’t give me reflection warnings.

(require '[clojure.java.io :as io])
​
(set! *warn-on-reflection* true)
(set! *print-meta* true)
​
(def file (io/file "hej"))
(.getName file) ;; reflection warning
​
(def generated-fn `(fn [~'file] (.getName ~'file)))
​
(eval generated-fn) ;; reflection warning 
​
(def generated-fn2 `(fn [~(with-meta 'file {:tag java.io.File})] (.getName ~'file)))
(prn generated-fn2) ;; (clojure.core/fn [^java.io.File file] (.getName file))
(eval generated-fn2) ;; reflection warning, but why?
​
(clojure.core/fn [^java.io.File file] (.getName file)) ;; no reflection warning

Here’s the problem on repl.it, if someone wants to give it a go there: https://repl.it/repls/ExcitedDetailedRedundancy

This should do the trick:

(def generated-fn2 `(fn [^java.io.File file#] (.getName file#)))
2 Likes

Huh, thanks a lot. Any idea why? I’m guessing what my code is doing is meta’ing the symbol being eval’d, rather than emitting a symbol with metadata, whereas your code somehow expresses that the metadata should be emitted? Thanks again, this is great.

Okay, next problem. I want to use this gensym inside another syntax-quote, how would I do this? I tried leting it etc, but having a hard time figuring it out.

(def generated-fn2 `(fn [^java.io.File file#]
                      [email protected](for [_ (range 3)]
                          `(.getName file#))))

This becomes two different symbols since it’s two different syntax-quotes.

For the first question, you need to quote java.io.File, otherwise it would be evaluated into the File class object (not a symbol).

For the second one, you’ll need to use gensym explicitly:

(def generated-fn2
  (let [file (gensym)]
    `(fn [~(with-meta file {:tag 'java.io.File})]
       [email protected](for [_ (range 3)]
           `(.getName ~file)))))
1 Like

Hey, thanks! That works wonders! Sadly can’t set two answers as the accepted.