4

假设我有以下关键字数组

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

那么问题是keysandkeys*得到了引用的符号'keys,而不是它所引用的变量中保存的解析值。

所以我的问题是:是否可以像普通的 lisp#.阅读器宏一样在读取时解析键的值,或者如果宏获得符号而不是列表文字,是否必须重新定义才能解析符号?

4

4 回答 4

1

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

(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])
于 2017-04-10T15:47:02.893 回答
1

您可以使用以下方法构建一个半麻烦的解决方案eval

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

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

这是由 mpened on the clojurians slack 提出的。

于 2017-04-10T12:54:51.503 回答
0

我有一个类似的问题,我试图动态注册一个规范。我遇到了麻烦,因为我没有传入文字关键字,所以解决方案是评估宏的参数,然后参考那些:

(def my-key ::this-is-a-test-key)

(defmacro define-spec [spec-key validator]
  (let [local-key (eval spec-key)]
    `(s/def ~local-key ~validator)))

(define-spec my-key int?)

;;testing it
(s/valid? my-key 123)
(s/valid? my-key "ASDASD")

还值得注意的是,在 cljs 中,调用define-spec会返回 nils,因为 cljs 不支持宏。

于 2021-09-10T22:54:33.590 回答
-1

或者你可以这样做:

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

可能需要使用合格的命名空间(例如::opt-un

spec/def

已经是一个宏;)

于 2017-04-10T20:01:11.253 回答