Clojure is the name fn that goes beyond its scope when it is composed ahead of schedule

I want to generate named functions with fn and return them from the macro, I tried the following example:

(defmacro getfn [namestr children] `(fn fn-name# [] (println "Recursing" ~namestr) (doall (map (fn [child#] (child#)) ~children)))) (def foo (getfn "foo" [])) (def bar (getfn "bar" [foo])) (defn -main [& args] (bar)) 

The result usually corresponds to the expected result:

 Recursing bar Recursing foo 

However, when I run this compiled lead (AOT), I get:

 Recursing bar Recursing bar ... Recursing bar Recursing bar Exception in thread "main" java.lang.StackOverflowError 

It seemed rather strange to me that the bar continues to call itself instead of foo, the only reasonable reason for this is the generated symbol fn-name# for leakage outside its area. Is this a bug in Clojure or the alleged behavior?

Update: For clarity, it should be mentioned that removing the fn-name# character and introducing an anonymous function fixes this problem. However, in my actual code, I sometimes need to call it recursively, so I need to call it.

+5
source share
1 answer

One solution to this problem is to use gensym to get a new character for each version of the macro, this will work by modifying getfn as follows:

 (defmacro getfn [namestr children] `(let [fn-name# (gensym)] (fn fn-name# [] (println "Recursing" ~namestr) (doall (map (fn [child#] (child#)) ~children))))) 

This is a little redundant, since by definition the name fn should be relevant only within its own domain.

Update: Just tested with alpha versions, and it seems that Clojure 1.7.0-alpha3 and later work without this hacking, Clojure 1.7.0-alpha2 and earlier do not work. Using this workaround is probably ok until a stable version 1.7.0 is released, unless someone comes up with something better.

+1
source

Source: https://habr.com/ru/post/1212082/


All Articles