开发者

Controlling symbol generation in Clojure macros

开发者 https://www.devze.com 2022-12-30 22:57 出处:网络
I\'m trying (as a self-learning exe开发者_JAVA百科rcise) to create a Clojure macro that will generate code to apply a function to a sequence of integers and sum the result, e.g.

I'm trying (as a self-learning exe开发者_JAVA百科rcise) to create a Clojure macro that will generate code to apply a function to a sequence of integers and sum the result, e.g.

f(0) + f(1) + f(2) + f(3)

This is my attempt:

(defmacro testsum [func n] 
  `(fn [x#] (+ ~@( map (fn [i] `(~func x#)) (range n)))))

However something seems to go wrong with the x# gensym and I end up with two different versions of x and hence the function doesn't work:

(macroexpand '(testsum inc 3))

gives:

(fn* ([x__809__auto__] 
  (clojure.core/+ 
    (inc x__808__auto__) 
    (inc x__808__auto__) 
    (inc x__808__auto__))))

This is pretty much exactly what I want apart from the different 809 and 808 versions of x.....

What am I doing wrong? I thought that the auto gensym was meant to create a single unique symbol for exactly this kind of purpose? Is there a better way of doing this?


foo#-style gensyms are valid only inside the syntax-quote where they were created. In your code, the two x#s are created in different syntax-quote blocks:

(defmacro testsum [func n] 
  `(fn [x#] (+ ~@( map (fn [i] `(~func x#)) (range n)))))
  ^- s-q1      ^-unquote       ^- s-q2

To fix this, use an explicit (gensym) call:

(defmacro testsum [func n]
  (let [x (gensym "x")]
    `(fn [~x] (+ ~@(map (fn [i] `(~func ~x)) (range n))))))

And the macro expansion ((macroexpand '(testsum inc 3))):

(fn* ([x4966] (clojure.core/+ (inc x4966) (inc x4966) (inc x4966))))
0

精彩评论

暂无评论...
验证码 换一张
取 消