I have created a macro which creates a named dispatcher
with 3 associates functions get-dispatcher
, set-dispatcher
and call-dispatcher
to work with the dispatcher (they get a dispatching function, add one or call one). It all works just fine! However, now I want to automate the related functions names creation, thus I am putting all these internals of the macro into a let
which defines that simple construction function. Note that in the code below only the get-
function's name is constructed with that automation. The set-
and call-
ones name creation still has that manual smell.
(defmacro create-dispatcher [name]
;creates a set of dispatching functions tagged
`(do
;define dispatcher
(def ~(symbol name) ~(atom {}))
(let
[name-w-prefix (fn [x] (~(symbol (str x "-" name))))]
; -- define getter
(defn (name-w-prefix "get")
"get-dispatcher [tag]: get a dispatcher fn by tag"
(~'[] (println "no tag is provided for '" ~(str name) "' dispatcher"))
(~'[tag]
(do
(println "dispatcher '" ~(str name) "' called with '" ~'tag "' tag")
; return the tagged dispatcher
( (keyword ~'tag) @~(symbol name) )))
)
; -- define caller
(defn ~(symbol (str "call-" name))
"get-dispatcher [tag & args]: call a dispatcher fn by tag and apply to the args"
~'[tag & args]
(apply (~(symbol (str "get-" name)) ~'tag) ~'args)
)
; -- define setter
(defn ~(symbol (str "set-" name))
~'[tag fn]
"add-dispatcher [tag fn]: add a dispatcher fn associated with the tag"
(swap! ~(symbol name) assoc (keyword ~'tag) ~'fn)
)
)
; -- report
(println "created dispatcher set for '" ~(str name) "' ok!")
))
However, there is a problem. The name-w-prefix
in the let
statement binding causes errors. How can I fix that?
(also any advices on improvement are welcome since I am a newb and that is almost the first thing that I wrote in Clojure)
All symbols in a macro are resolved in the current namespace and expected to evaluate to a var. You could quote the name-w-prefix
symbol, but that would risk colliding with symbols passed in to the macro during macro expansion. So, Clojure provides a special syntax for use in syntax-quoted forms for generating symbols - just append a #
to the end of the symbol and Clojure will treat that as a quoted, auto-generated symbol. So in this case, replace occurrences of name-w-prefix
with name-w-prefix#
and you should be good to go.
Taking a step back and looking at what your overall goal is, I think you should move the name-w-prefix
definition outside the syntax quotes and then use syntax-escape to call it. Otherwise, you'll get still more errors because defn
requires a symbol, so once expanded the macro must produce a defn
form that has a symbol as its second item. Something along the lines of:
(defmacro create-dispatcher [name]
(let [name-w-prefix #(symbol (str % "-" name))]
`(do
(def ~(symbol name) (atom {}))
(defn ~(name-w-prefix "get")
([] (println "no tag provided"))
([tag#] (println "called with tag" tag#))))))
Note that I've changed ~'[tag]
to [tag#]
in the defn
body in accordance with what I was talking about above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With