Share your thoughts in the 2024 State of Clojure Survey!

Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Macros by

I'm trying to create an atom in a macro function and embed a reference to it in the macro output form:

(defmacro x []
  (let [a (atom {})]
    `(fn []
       ~a)))

an attempt to use this macro results in this error:

`

(x)
Syntax error compiling fn* at (/tmp/form-init15982565344292085756.clj:1:1).
Can't embed object in code, maybe print-dup not defined: clojure.lang.Atom@350e9f50

`

I tried defining an empty print-dup overload but it resulted in a different error:

`

(defmethod print-dup clojure.lang.Atom [x w])
(x)
Syntax error compiling fn* at (/tmp/form-init15982565344292085756.clj:1:1).
Can't embed unreadable object in code: clojure.lang.Atom@5f3623b3

`

I would expect this to work since clojure has eval. What is the issue here?

1 Answer

+2 votes
by
selected by
 
Best answer

Macros are functions that take code and return code. Atoms are stateful objects - there is no way to represent them with identity in code so this is not going to work.

What are you actually trying to do?

by
edited by
>Macros are functions that take code and return code. Atoms are stateful objects - there is no way to represent them with identity in code so this is not going to work.

I'm not sure I understand what this means.

>What are you actually trying to do?

The macro will return a function which will modify the atom values when evaluated, and some other code will read those values. This is a better example:

  (defmacro x []
    (let [a (atom {})
          s (gensym)]
      `(fn []
         (let [~s ~a]
           ))))
by
How is the above example different from:

(defmacro x []
  (let [a (vec (range 3))
        s (gensym)]
    `(fn []
       (let [~s ~a]
         ))))

Which works.
by
In your second example, both a and s have representations as code (as a vector and symbol). An atom is a stateful object with identity and does not.

In general it's not going to be useful to construct an object at compile time and then try to use it at runtime (as things like aot compilation may separate those completely).

You can move the construction of the atom into the emitted code (inside the ` here) so that you are emitting the code to construct at runtime. I'm not sure how the rest of this macro is useful though.

What are you actually trying to do?
...