使用宏和doseq来生成spec

Log*_*ter 2 macros clojure clojure.spec

我发现自己写了很多这样的规范:

(s/def ::name string?)
(s/def ::logUri string?)
(s/def ::subnet (s/and string? #(> (count %) 5)))
(s/def ::instanceType string?)
...
(s/def ::key (s/and string? #(> (count %) 5)))
(s/def ::instanceCount string?)
(s/def ::bidPct string?)
Run Code Online (Sandbox Code Playgroud)

即很多s/ands/def。这似乎是一种浪费。所以我决定编写一个宏来为我完成这个任务。就像是:

(defmacro and-spec [validations]
  (doseq [[keyname & funcs] validations]
    `(s/def ~keyname (s/and ~@funcs))))
Run Code Online (Sandbox Code Playgroud)

所以我可以做类似的事情:

(and-spec [[::name1 [string?]]
           [::name2  [string? #(> (count %) 5)]]])
Run Code Online (Sandbox Code Playgroud)

这将为s/def我做所有的事情。不幸的是,上面的宏不起作用,但我不知道为什么。

(s/valid? ::name1 "asdf")
Execution error at emr-cli.utils/eval6038 (form-init17784784591561795514.clj:1).
Unable to resolve spec: :emr-cli.utils/name1
Run Code Online (Sandbox Code Playgroud)

这项工作的较小版本:

(defmacro small-and-spec-works [key-name validations]
  `(s/def ~key-name (s/and ~@validations)))
=> #'emr-cli.utils/tsmall
(and-spec-small-works ::mykey [string?])
=> :emr-cli.utils/mykey
(s/valid? ::mykey "asdf")
=> true
Run Code Online (Sandbox Code Playgroud)

但当我引入 let 绑定时,事情就开始变得奇怪了:

(defmacro small-and-spec [validation]
  (let [[key-name & valids] validation]
    `(s/def ~key-name (s/and ~@valids))))
=> #'emr-cli.utils/small-and-spec
(small-and-spec [::mykey2 [string?]])
=> :emr-cli.utils/mykey2
(s/valid? ::mykey2 "asdf")
Execution error (IllegalArgumentException) at emr-cli.utils/eval6012 (form-init17784784591561795514.clj:1).
Key must be integer
Run Code Online (Sandbox Code Playgroud)
  1. 我怎样才能让doseq宏起作用?
  2. small-and-spec造成错误的原因是什么Key must be integer

ako*_*ond 5

(defmacro and-spec [defs]
    `(do
        ~@(map (fn [[name rest]]
              `(s/def ~name (s/and ~@rest))) defs)))
Run Code Online (Sandbox Code Playgroud)

doseq是为了副作用。它总是会回来nil

  • “我不希望宏返回定义列表。” 实际上是你想要的,但在做 (3认同)
  • 具体来说,因为仅创建源代码列表不会导致对其进行评估。宏的“契约”是它返回的任何代码都将被评估。它作为副作用产生然后丢弃的代码自然会被丢弃。 (2认同)