Reputation: 2259
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?
Upvotes: 0
Views: 65
Reputation: 9930
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)))
Upvotes: 1