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#]
                      ~@(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})]
       ~@(for [_ (range 3)]
           `(.getName ~file)))))
1 Like

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

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.