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 '[ :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})] (.getName ~'file)))
(prn generated-fn2) ;; (clojure.core/fn [^ file] (.getName file))
(eval generated-fn2) ;; reflection warning, but why?
(clojure.core/fn [^ file] (.getName file)) ;; no reflection warning

Here’s the problem on, if someone wants to give it a go there:

This should do the trick:

(def generated-fn2 `(fn [^ file#] (.getName file#)))

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 [^ 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, 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 '})]
       [email protected](for [_ (range 3)]
           `(.getName ~file)))))
1 Like

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