我正在使用 Clojure 1.11.1
版本的 spec.alpha
。
(require '[clojure.spec.alpha :as s])
添加代表“别名”另一个规范的方式(直接、s/spec、s/and)的三个规范 ::foo
。
(s/def ::foo int?)
(s/def ::bar ::foo)
(s/def ::bar-spec (s/spec ::foo))
(s/def ::bar-and (s/and ::foo))
只有 ::bar-and
规范保留了别名信息。
(s/form ::bar) ;=> clojure.core/int?
(s/form ::bar-spec) ;=> clojure.core/int?
(s/form ::bar-and) ;=> (clojure.spec.alpha/and :user/foo)
我发现这很有用,例如,“查找” ::foo
规范的用法非常简单。
添加将上述规范汇总的规范 ::m
。
(s/def ::m (s/keys :req [::foo ::bar ::bar-spec ::bar-and]))
对于 ::bar-spec
别名,覆盖底层 ::foo
规范的数据生成器不起作用。
(gen/generate (s/gen ::bar {::foo (fn [] (gen/return 0))})) ;=> 0
(gen/generate (s/gen ::bar-spec {::foo (fn [] (gen/return 0))})) ;=> -2
(gen/generate (s/gen ::bar-and {::foo (fn [] (gen/return 0))})) ;=> 0
(gen/generate (s/gen ::m {::foo (fn [] (gen/return 0))}))
;=> #:user{:foo 0, :bar 0, :bar-spec 31, :bar-and 0}
在直接别名 ::bar
的情况下,覆盖别名规范的生成器不起作用。
(gen/generate (s/gen ::bar {::bar (fn [] (gen/return 0))})) ;=> -492158
(gen/generate (s/gen ::bar-spec {::bar-spec (fn [] (gen/return 0))})) ;=> 0
(gen/generate (s/gen ::bar-and {::bar-and (fn [] (gen/return 0))})) ;=> 0
(gen/generate (s/gen ::m
{::bar (fn [] (gen/return 0))
::bar-spec (fn [] (gen/return 0))
::bar-and (fn [] (gen/return 0))}))
;=> #:user{:foo 183249700, :bar -1, :bar-spec 0, :bar-and 0}
覆盖 ::bar
的生成器的唯一方法是覆盖 ::foo
。但这也会影响所有 ::foo
的别名,这并不总是所期望的。
我最喜欢 s/and
的行为,但它对于其他用户来说可能是最令人困惑的,因为它的定义听起来应该有多个参数。
其他两种方法行为的理由是什么?
这是预期的吗?
还有其他定义别名的方法吗?
在未来的规范中会进行更改吗?