在Clojure中将符号传递给宏之前,是否可以将其解析为其值?

在Clojure中将符号传递给宏之前,是否可以将其解析为其值?,clojure,clojure.spec,Clojure,Clojure.spec,假设我有以下关键字数组 (def keys [::description ::url ::mailing-list]) 我想在两个规范中重复使用它;一个用于定义映射,另一个用于定义函数的可选参数 (require '[clojure.spec :as spec]) (spec/def ::project-map (spec/keys :opt-un keys)) (spec/def ::project-args (spec/keys* :opt-un keys))

假设我有以下关键字数组

(def keys [::description ::url ::mailing-list])
我想在两个规范中重复使用它;一个用于定义映射,另一个用于定义函数的可选参数

(require '[clojure.spec :as spec])

(spec/def ::project-map
      (spec/keys :opt-un keys))

(spec/def ::project-args
      (spec/keys* :opt-un keys))
问题是
键*
传递的是带引号的符号
的键
,而不是它引用的变量中保存的解析值


因此,我的问题是:是否可以像常见的lisp
#
reader宏那样在读取时解析键的值,或者如果宏得到的是符号而不是列表文字,则必须重新定义宏才能解析符号?

您可以使用
eval
构建一个半讨厌的解决方案:

(spec/def ::project-map
  (eval `(spec/keys :opt-un ~keys)))

(spec/def ::project-args
  (eval `(spec/keys* :opt-un ~keys)))

这是mpeed在clojurians slack中提出的。

您也可以将它们包装在另一个宏中,因为宏负责如何计算它们的参数

(defmacro def-both [name name* keys]
  `(do (s/def ~name (s/keys :opt-un ~keys))
       (s/def ~name* (s/keys* :opt-un ~keys))))

(user/def-both ::project-map ::project-args [::description ::url ::mailing-list])
或者你可以这样做:

(spec/def ::project-map (spec/keys ::opt-un ~keys))
可能需要使用限定的名称空间(例如
::opt un


已是宏;)

我不明白为什么会这样。由于
::opt un
:my namespace/op un
的缩写,因此它与命名的arg
:opt un
的匹配程度不应超过
:opt un
。如果我们查看失败的断言,它会检查命名参数的值是否为限定关键字,而不是命名参数本身:它不起作用。看看从规范生成时得到的结果。
::opt un
被忽略,这就是为什么没有检查~keys的内容。clojure可以很好地使用,但不幸的是Clojurescripthm不能使用,不幸的是规范太重了。是的,我最终使用了一个宏。我希望我可以扩展这一符号,但不必包装在宏中。
spec/def