Вопрос

I can't think of any possible use case for this, but as an exercise to try to wrap my mind further around Clojure's macros, I'm trying to write a macro that will swap the values assigned to two symbols.

Here are two things that I tried:

Method 1:

(defmacro swap [x y]
  `(let [tmp# ~x]
     (def x ~y)
     (def y ~tmp#)))

Method 2:

(defmacro swap [x y]
  `(let [tmp# ~x]
     (alter-var-root #'x (fn [] ~y))
     (alter-var-root #'y (fn [] ~tmp#))))

Here is the code I use to test it:

(def four 4)
(def five 5)

(swap four five)

(printf "four: %d\nfive: %d" four five)

Expected output:

four: 5
five: 4

However, using either version of the macro, I get a java.lang.RuntimeException: Unable to resolve symbol: tmp# in this context. Am I using auto gensym incorrectly?

Using method 1, I was able to get it to run by changing the last line to (def y tmp#))) (taking out the ~ before tmp#), however I get the output four: 4\nfive: 5 which is not swapped.

Это было полезно?

Решение

Ignoring the fact that mutating vars like this is a bad idea, let's assume you really want to do it anyway. Your problem is two-fold:

  • You have an unquote on ~tmp#, where you just want tmp#
  • You're missing an unquote on x and y: you want to (def ~x ~y) and (def ~y tmp#)

The version you wrote always assigns to the vars named x and y, instead of modifying the vars provided by the user.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top