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#]
                      ~@(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 '})]
       ~@(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.