【问题标题】:Clojure: Defining a Macro that Uses the Carret (^{}) Metadata SyntaxClojure:定义使用 Carret (^{}) 元数据语法的宏
【发布时间】:2026-01-07 10:05:01
【问题描述】:

我正在尝试定义这样的宏:

(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)))))

但是,我收到此编译错误:

未处理的 java.lang.IllegalArgumentException 元数据必须是 符号、关键字、字符串或映射

我认为这很正常。但是,我不确定如何使这样的宏工作,或者是否可能。

注意:我不想使用with-meta。阅读此其他问题的解决方案的修订以了解原因:Clojure: issues getting the symbol of a function in a looping context

我使用 with-meta 的唯一原因是,如果你有办法让它返回一个非 AFunc 标识符。

【问题讨论】:

  • 宏是干什么用的?也许还有另一种方法可以完成您的需求,您能否更具体地了解用例?
  • @DiegoBasch 我需要的是一个创建函数的宏。然后我想将元数据附加到该函数对象(而不是它的 Var)。但是,如果我使用with-meta 而不是^{},那么我会得到#< clojure.lang.AFunction$1@15ab1764> 作为该对象的引用,而不是像#<core$foo user.core$foo@66e37466> 这样更易于阅读的东西。即使文档说这两种方法没有区别,区别似乎是with-meta返回的内容与^{}宏不一样(不是同一个对象引用)
  • @DiegoBasch 告诉我是否清楚,否则我将通过更新问题本身再次尝试。
  • 您必须在 ^ 之后包含错误中的一项,因为 ^ 是阅读器宏。您可以执行 ^{~meta-key ~meta-val} 之类的操作,或者使用字符串获取 {:tag "something"}。我的问题更多是关于为什么需要使用宏生成函数,因为在该特定函数中没有任何内容需要宏。
  • 我不确定^{~meta-key ~meta-val} 会起作用,因为meta-data 参数本身就是一个映射(用于向函数提供元数据。我需要做的是让人们创建一系列函数,但具有特定模式。所以我使用宏来强制遵循该特定模式。我的问题是我让他们使用地图定义所有必要的元数据。但是,我认为我可以将关键字参数用于常见和更静态的元数据值,然后使用可选的元数据参数,如果需要,他们可以添加更多。

标签: macros clojure


【解决方案1】:

抱歉,您确实想使用with-meta,您只想在宏中使用它,而不是在返回的代码中使用它。

通过阅读^{:foo :bar} (fn []) 获得的形式与通过评估(with-meta `(fn [])) 获得的形式大致相同,因此这就是您想要在宏中执行的操作:在生成代码时评估with-meta 调用。

(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))))

调用(foo-macro fn-name {:foo "bar"}) 应该会给你你想要的:一个带有(meta fn-name) 的函数返回{:foo "bar"},当它被打印出来时,会出现类似#<user$fn_name user$fn_name@35df85a1> 的东西。

【讨论】: