Minor macro help

2 views
Skip to first unread message

samppi

unread,
Aug 3, 2009, 5:43:04 PM8/3/09
to Clojure
I'm trying to make a macro my-macro so that the last statement in:

(def my-map {:a 2})
(defn process-a-map [arg] ...)
; Turns maps into corresponding vectors.
; (process-a-map my-map) returns ['*foo* :blah].

(my-macro a-map ; Last statement
(do-something))

expands to:

(binding [*foo* :blah] (do-something))
; process-a-map was called on a-map

But I'm getting stuck because a-map always gets passed into my-macro
as a symbol.

(defmacro my-macro [a-map & forms] ; Naive implementation
`(binding ~(vec (process-a-map a-map)) ~@forms))

Here, when my-macro is called, process-a-map receives the symbol 'a-
map, rather than what I want—a-map's value, {:a 2}. What can I do? Is
this impossible for macros to deal with?

CuppoJava

unread,
Aug 3, 2009, 8:01:57 PM8/3/09
to Clojure
You can use eval to retrieve the value of a-map when the macro is
expanded.

(let [value (eval a-map)]
`(binding ~(vec (process-a-map value)) ~@forms))

I've programmed some substantial programs now in Clojure though, and
I've never had to use this. Perhaps there is another way to achieve
what you want?

As a rule of thumb that I use, using a macro should only look like a
syntax extension. There shouldn't be any dependance on the values of
any of the arguments.

Hope this helps
-Patrick

John Harrop

unread,
Aug 3, 2009, 7:47:43 PM8/3/09
to clo...@googlegroups.com
On Mon, Aug 3, 2009 at 5:43 PM, samppi <rbys...@gmail.com> wrote:
I'm getting stuck because a-map always gets passed into my-macro
as a symbol.

 (defmacro my-macro [a-map & forms] ; Naive implementation
   `(binding ~(vec (process-a-map a-map)) ~@forms))

Try

(defmacro my-macro [a-map & forms] ; Naive implementation
  `(binding ~(vec (process-a-map ~a-map)) ~@forms))

If you want something included not as a symbol, precede it with a ~ (does the same thing , does in common lisp). You already have ~@forms which just does the same thing, except that if forms is a list it is appended to the list it's in rather than inserted as a single item.

If bar = (+ 2 3):

`(foo bar) => (foo bar)
`(foo ~bar) => (foo (+ 2 3))
`(foo ~@bar) => (foo + 2 3)

Takes some getting used to, but once you master Lisp macros, it's Katie bar the door.

John Harrop

unread,
Aug 3, 2009, 8:14:32 PM8/3/09
to clo...@googlegroups.com
Eh, I hadn't noticed that; he's using process-a-map already in a ~ expression. That's the sort of thing more usually done in a helper function:

(defn binding-from-map [a-map forms]
  `(binding ~(vec (process-a-map a-map)) ~@forms)))

Then call from a macro:

(defmacro foo [map & body]
  (binding-from-map map body))

(foo {:a 1} (fn1 arg1) (fn2 arg2 arg3))

If you really want to call the eventual macro with a non-literal map, though, then you'll need it to eval that argument as Patrick says.

Meikel Brandmeyer

unread,
Aug 4, 2009, 2:10:47 AM8/4/09
to Clojure
Hi,

On Aug 3, 11:43 pm, samppi <rbysam...@gmail.com> wrote:

>   (def my-map {:a 2})
>   (defn process-a-map [arg] ...)
>     ; Turns maps into corresponding vectors.
>     ; (process-a-map my-map) returns ['*foo* :blah].
>
>   (my-macro a-map ; Last statement
>     (do-something))

The answer which should leave the least grey hair is:
this is not possible. While it is true, that you can use
`eval` to get the map as the others pointed out, you
don't want to do that. Suppose you had my-macro with
`eval`.

(let [my-map {:a 2}]
(my-macro my-map (do-something)))

(defn my-fancy-fn
[the-map-as-arg]
(my-macro the-map-as-arg (do-something)))

Neither of these two forms work and this is a serious
limitation because you are tied to global variables....

>   (binding [*foo* :blah] (do-something))

For this special case however you can solve the issue
as follows:

(defn my-macro*
[the-map thunk]
(clojure.lang.Var/pushThreadBindings the-map)
(try
(thunk)
(finally
(clojure.lang.Var/popThreadBindings))))

(defmacro my-macro
[the-map & body]
`(my-macro* ~the-map (fn [] ~@body)))

Hope this helps.

Sincerely
Meikel

John Harrop

unread,
Aug 4, 2009, 2:43:49 AM8/4/09
to clo...@googlegroups.com
Very clever, Meikel.
Reply all
Reply to author
Forward
0 new messages