Neoasimov
Neoasimov

Reputation: 1111

Clojure: Defining a Macro that Uses the Carret (^{}) Metadata Syntax

I am trying to define a macro like this:

(defmacro foo-macro
  "[name] is the name of the function to define
   [meta-data] is the map that defines the meta-data of the function"
  [name meta-data]
  (let [vals (gensym "label-")]
    `(def ~name
       ^@~meta-data
       (fn [~vals] (eval nil)))))

However, I am getting this compilation error:

Unhandled java.lang.IllegalArgumentException Metadata must be Symbol,Keyword,String or Map

I think this is normal. However, I am not sure how I can make such a macro to work, or if it is even possible.

Note: I do not want to use with-meta. Read the revision of the resolution of this other question to understand why: Clojure: issues getting the symbol of a function in a looping context.

The only reason why I would use with-meta, is if you have a way to make it return a non-AFunc identifier.

Upvotes: 0

Views: 283

Answers (1)

mange
mange

Reputation: 3212

I'm sorry, but you do want to use with-meta, you just want to use it within your macro rather than within the code you return.

The form that you get by reading ^{:foo :bar} (fn []) is approximately the same as what you get by evaluating (with-meta `(fn [])), so that's what you want to do in your macro: evaluate the with-meta call while generating code.

(defmacro foo-macro
  "[name] is the name of the function to define
   [meta-data] is the map that defines the meta-data of the function"
  [name meta-data]
  (let [vals (gensym "label-")]
    `(def ~name
       ~(with-meta `(fn [~vals] (eval nil))
          meta-data))))

Calling (foo-macro fn-name {:foo "bar"}) should give you what you want: a function with (meta fn-name) returning {:foo "bar"} which, when printed, comes out something like #<user$fn_name user$fn_name@35df85a1>.

Upvotes: 0

Related Questions