简体   繁体   中英

unquoting argument in macro doesn't work as expected

I have the following Clojure macro:

(defmacro with-model
  [ref & body]
  `(tx
     (let [ds# (when (vector? (first ~body)) (ffirst ~body))
           name# (when (vector? (first ~body)) (second (first ~body)))
           ~ref (model ds# name#)]
       (do ~@body))))

and I'm trying to use it like this:

(deftest with-model-test
  (with-model sandwich
    (let [nodes (-> sandwich .listObjects iterator-seq)]
      (is nodes))))

or this:

(deftest with-model-test
  (with-model sandwich [*ds* "named-model"]
    (let [nodes (-> sandwich .listObjects iterator-seq)]
      (is nodes))))

The idea being that sandwich should now refer to a Model , but I get a runtime exception:

Unable to resolve symbol: sandwich in this context

If I (println ~ref) in the macro, I get the model instance. If I (println '~ref) I get sandwich . How should I be going about this instead?

The macroexpansion when using the with-model macro as (with-model sandwich (let [node (-> sandwich)])) looks like this (with namespaces removed, let binding names shortened and some formatting):

(macroexpand-1 '(with-model sandwich (let [node (-> sandwich)])))

(tx
 (let [ds   (when (vector? (first ((let [node (-> sandwich)]))))
              (ffirst ((let [node (-> sandwich)]))))
       name (when (vector? (first ((let [node (-> sandwich)]))))
              (second (first ((let [node (-> sandwich)])))))
       sandwich (model ds name)]
  (let [node (-> sandwich)])))

As you can see sandwich is being used in the let before it is defined, since the macro generates code that figures stuff out about the second argument after the expansion. A way to go around this is to have the macro figuring things out before the expansion. In general I try to do this in order to have a simpler expansion even though it sometimes implies a more complicated macro code, not in this case tough.

(defmacro with-model
  [ref & [x & _ :as body]]
  `(tx
     (let [ds# ~(when (vector? x) (first x))
           name# ~(when (vector? x) (second x))
           ~ref (model ds# name#)]
       ~@body)))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM