1
votes

I want to achieve to return a namespace resolved symbol in a macro that has recently defined the symbol itself:

(ns my-namespace)

(defmacro def&resolve [a b]
  `(do
     (def ~a 12)
     (def ~b ~(resolve a))))

(def&resolve foo bar)
bar ;=> nil

In the example bar returns nil. I however, want it to return my-namespace.foo. I know this is not working since at the time resolve is called, a isn't defined yet. I need a to be namespace-resolved at macro-expansion time for my use case [1], however.

In the next run I wanted to declare a at macro expansion time (does that even make sense?) and, thus, force that it is present in the current ns when calling resolve.

(defmacro def&resolve [a b]
  (declare a)
  `(do (def ~a 12)
       (def ~b ~(resolve a))))

(def&resolve foo bar)
bar ;=> nil

Doesn't work either.

How can I def something and at the very macro expansion namespace resolve it?


[1] My use case is the following: I want to define a bundle of functions and add it to the meta information of a def-binding (thus, I need it at macro expansion time). Currently, I only store the symbols, without the namespace the functions were defined in.

3

3 Answers

0
votes
(ns another-namespace)

(defmacro def&resolve [a b]
  `(do
     (def ~a 12)
     (def ~b (resolve '~a))))

(macroexpand-1 '(def&resolve foo bar))
#=> (do (def foo 12) (def bar (clojure.core/resolve (quote foo))))

(def&resolve foo bar)
bar
#=> #'another-namespace/foo
0
votes

since the var will be def'ed/created at run time, once the macro has finished it's job and gone away, the macro can instead predict what the var will be named and create the symbol directly rather than making a round trip through the namespace and back

Let's do it manually first

user> (def foo)
#'user/foo
user> (resolve 'foo)
#'user/foo

that looks right, now let's do it by creating the symbol:

user> (symbol (str *ns* "/" "foo"))
user/foo

and copy that into the macro definition:

user> (defmacro def&resolve [a b]
        `(do
           (def ~a 12)
           (def ~b ~(symbol (str *ns* "/" "foo")))))
#'user/def&resolve

and test it out

user> (def&resolve foo bar)
#'user/bar

user> bar
12

This method is preferable to the approach of using intern to create the symbol in the namespace using a dummy value, at macro expand time, then resolveing it also at macro expansion time. Resulting in the same value. The advantage of this approach is that it does make resolve work at macro expand time as in your original question

user> (do (intern *ns* 'a :dummy-value)
          (resolve 'a))
#'user/a

though i recommend going with the symbol generation approach.

0
votes

Nesting of syntax quote helps, with additional quoting of ~a:

(defmacro def&resolve
  [a b]
  `(do
     (def ~a 12)
     (def ~b ~`(resolve '~a))))
#'test-project.core/def&resolve

test-project.core=> (macroexpand '(def&resolve foo bar))
(do (def foo 12) (def bar (clojure.core/resolve (quote foo))))

test-project.core=> (def&resolve foo bar)
#'test-project.core/bar

test-project.core=> foo
12

test-project.core=> bar
#'test-project.core/foo

If you actually want to get value of foo into the bar, then you have to deref (@) var you get by resolving:

(defmacro def&resolve
  [a b]
  `(do
     (def ~a 12)
     (def ~b @~`(resolve '~a))))

test-project.core=> (def&resolve foo bar)
#'test-project.core/bar
test-project.core=> bar
12