Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Spec by
edited by

I'm using spec.alpha from Clojure 1.11.1.

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

Add spec ::foo with three specs that represent ways to "alias" another spec (direct, 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))
  • Problems with documentation

Only ::bar-and spec preserves the information about aliasing.

(s/form ::bar)      ;=> clojure.core/int?
(s/form ::bar-spec) ;=> clojure.core/int?
(s/form ::bar-and)  ;=> (clojure.spec.alpha/and :user/foo)

I find that useful, e.g. it's trivial to "find usages" of the ::foo spec.

  • Problems with generator overriding

Add spec ::m which aggregates specs from above.

(s/def ::m (s/keys :req [::foo ::bar ::bar-spec ::bar-and]))

Overriding data generator for underlying ::foo spec doesn't work for ::bar-spec alias.

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

Overriding generator for alias spec doesn't work in the case of direct alias ::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}

The only way to override the generator for ::bar is to override it for ::foo. But that also affects all aliases of ::foo, which is not always desirable.


I like the behavior of s/and the most but its definition is arguably the most confusing for other users, because it sounds like s/and should have multiple arguments.

What is the rationale for behavior of other two approaches?
Is that expected?
Is there any alternative way to define aliases?
Will this be changed in the future spec?

1 Answer

0 votes
by

its definition is arguably the most confusing for other users, because it sounds like s/and should have multiple arguments.

That's exactly the case. s/and is similar to and - the most common usage is with multiple arguments, although you can pass just one, or even none.

...