我在使用Clojure 1.11.1
中的spec.alpha
。
(require '[clojure.spec.alpha :as s])
添加spec ::foo
,其中包含三个表示"别名"另一个spec的方式的spec(直接、s/spec、s/and)。
(s/def ::foo int?)
(s/def ::bar ::foo)
(s/def ::bar-spec (s/spec ::foo))
(s/def ::bar-and (s/and ::foo))
只有::bar-and
spec保留关于别名的信息。
(s/form ::bar) ;=> clojure.core/int?
(s/form ::bar-spec) ;=> clojure.core/int?
(s/form ::bar-and) ;=> (clojure.spec.alpha/and :user/foo)
我发现这很有用,例如,"查找::foo
spec的使用情况"很简单。
添加spec ::m
,它聚合来自上方的spec。
(s/def ::m (s/keys :req [::foo ::bar ::bar-spec ::bar-and]))
覆盖底层::foo
spec的数据生成器对::bar-spec
别名不起作用。
(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
的情况下,覆盖别名spec的生成器不起作用。
(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
的行为,但它的定义可能是最令人困惑的,因为听起来像s/and
应该有多个参数。
这两类方法行为背后的原理是什么?
这是预期的吗?
有没有其他定义别名的方法?
这是否会在将来的spec中得到修改?