2

I have a question regarding how to define functions/macros which call other macros or special forms but where one of the symbols passed in needs to be dynamic.

The simplest version of this question is described below:

We can define variables using def

(def x 0)

But what if we wanted the name x to be determined programmatically so that we could do the equivalent of?

(let [a 'b]
  (our-def a 3)) => user/b

We could try to define a function

(defn defn2 [sym val]
  (def sym val))

However it does not do what we want

(def2 'y 1)   => #'user/sym

At first it seems like a macro works (even though it seems like it would be unnecessary)

(defmacro def3 [sym val]
  `(def ~sym ~val))

(def3 z 2) => user/z

but it is just superficial, because we're really right back where we started with regular def.

(let [a 'b]
  (def3 a 3)) => user/a

I can do it if I use eval, but it doesn't seem like eval should be necessary

(defn def4 [sym val]
  (eval `(def ~sym ~val)))

(let [a 'b]
  (def4 a 4)) => user/b

If there are other built-in commands that could achieve this particular example, they are not really what I am looking for since def is just to show a particular example. There are macros more complicated than def that I might want to call and not have to worry about how they were internally implemented.

WuHoUnited
  • 8,279
  • 3
  • 24
  • 27

1 Answers1

1

First: The right way to do this is to use macro that starts with def... since this is the way people have been doing defs and is of little surprise to the user.

To answer you question: Use intern:

(def foo 'bar)
(intern *ns* foo :hi)
(pr bar) ;; => :hi
(intern *ns* foo :hi2)
(pr bar) ;; => :hi2

If you want to use macros do this:

(def z 'aa)
(defmacro def3 [sym val]
  `(def ~(eval sym) ~val))
(def3 z 2)
(pr aa) ;; => 2
ClojureMostly
  • 4,652
  • 2
  • 22
  • 24
  • Hi Andre, Thank you for taking the time to answer, but as the question points out, I'm looking to know if there is a way to do this using `def` and not with other functions such as `intern` because there are many other similar macros (`defn`, `defmacro`, `defmulti`, `defmethod`, `defrecord`,`clojure.core.logic/defne`) and while most of these have pretty small bodies which could probably be re-implemented as a function, they or other library macros could have defined them in one enormous macro like the `for` macro, or the function they are defined in terms of might be part of a 'private' API. – WuHoUnited Aug 16 '15 at 00:56
  • `(partial intern *ns*)` seems to be exactly the function you tried to define in the question. My understanding now is that your question really has nothing to do with `def` per se. What you're asking is how to pass an evaluated argument to a macro that normally wouldn't evaluate it. Is that correct? – Mars Aug 16 '15 at 07:06
  • btw I don't think it would be hard to define a simple function version of `defn` using the above function version of `def`. The others may be more difficult. – Mars Aug 16 '15 at 07:12
  • I'm not sure what you're trying to do. Maybe this is an XY problem: Are you just playing around for fun or are you actually trying to use this for something specific? If yes, what? Regarding macros: They're evaluated by the compiler, so if you can get the actual symbol during your compile phase then there is nothing wrong with emitting a `(def ~(eval sym) ~val)`. – ClojureMostly Aug 16 '15 at 13:07
  • Hi Mars and Andre, It started out as something specific (like Mars mentioned it wasn't `def` in that particular case) but I solved that problem by using `eval` around the form and from that point on it became 'I really want to know if this generally can be solved without `eval` or reverse engineering the macro I wish to call in order to use `intern`'. – WuHoUnited Aug 16 '15 at 16:29