Search code examples
macrosclojureclojure-java-interop

Issue with clojure macro returning map with java object


I'm very new to macros so I have a basic question that seems to do with the way that Java objects are represented in Clojure code. I have a macro that should return a map:

(defmacro g []
  (let [m {:color :black}]
     `(identity ~m)))


user=> (g)
{:color :black}

However when I use a java object in the map, things get weird

(defmacro g []
  (let [m {:color java.awt.Color/BLACK}]
     `(identity ~m)))

 user=> (g)
 CompilerException java.lang.RuntimeException: Can't embed object in code, maybe
 print-dup not defined: java.awt.Color[r=0,g=0,b=0], compiling:(NO_SOURCE_PATH:1
 :1)

If I change defmacro to defn the output looks like this:

user=> (g)
(clojure.core/identity {:color #<Color java.awt.Color[r=0,g=0,b=0]>})

So I'm guessing the representation is borking up the reader. Is there a correct way to do this?


Solution

  • There are several ways to write this though one way that strikes me as appropriately macro-ish is to have it return a form that evaluates to the map including the let.

    user> (defmacro g []
              `(let [m# {:color java.awt.Color/BLACK}]
                     (identity m#)))
    #'user/g
    user> (g)
    {:color #<Color java.awt.Color[r=0,g=0,b=0]>}
    

    This allows the object to be resolved from the class.