开发者

Unquote-splice and wrap in vector in a clojure macro

I have recently started learning clojure and am reading The Joy of Clojure to get to grips with it. I have a question regarding a code segment in the Macros chapter (8), on page 166

(defmacro domain [name & body]
  `{:tag :domain,                      ;`
    :attrs {:name (str '~name)},       ;'
    :content [~@body]})

As I understand it, body is a sequence like structure with all arguments except the first one. If so, in the third line, why are we unquote-splicing (~@) and putting the values in a vector again. Why not just do ~body instead of [~@body]? What is the difference?

I am sorry but I am finding it really hard to grasp the whole macros thingy (coming from pyth开发者_运维百科on).

Edit: After a bit of experimentation, I found that this works,

(defmacro domain2 [name & body]
  `{:tag :domain,                      ;`
    :attrs {:name (str '~name)},       ;'
    :content '~body})

and along with the results I got from Joost's answer, I think I know what is happening here. body is being represented as a list, and so if I don't put a ' in front of ~body, clojure will try to evaluate it.

user=> (domain "sh" 1 2 3)
{:content [1 2 3], :attrs {:name "sh"}, :tag :domain}

user=> (domain2 "sh" 1 2 3)
{:content (1 2 3), :attrs {:name "sh"}, :tag :domain}


I think that the answer lies in the intention of this macro.

Quickly looking at the mentioned page it seems the idea is to create a data-structure for the domain using a map. The structure chosen is identical to the one used by clojure.xml library.

It is true that emit function will produce similar results both with your code and the one from the book, but as the function parse from clojure.xml produces a map with content in a vector, it is better to do the same here, in order not to break other code relying on the same structure. It might be a good idea to also use a struct here to be consistent with clojure.xml, as now for example (content (domain ...)) doesn't work.

Thinking of data structures in general i find it a good idea to use an indexed sequence like vector here as it makes it possible to say for example ((:content domain-item) 1) to access second item of content. Not to mention subsequences etc.


You're quite right; ~body is already a sequence, so unless there's a need to guarantee that :content is a vector (instead of just something that can be seq'ed) the [~@body] expression can be replaced by just ~body.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜