Fork me on GitHub
#clojure-spec
<
2018-01-13
>
kenny00:01:32

The asymmetry does not make sense to me. Either both should throw or both be allowed.

seancorfield02:01:26

The (s/keys :req (conj my-keys ::b)) is treated as (s/keys :req [conj my-keys ::b]) so it requires ::b but the other two elements are symbols so s/keys probably doesn't handle them as expected (it certainly does not evaluate them).

seancorfield02:01:54

Hence, when you supply a map containing ::b it validates and when it doesn't contain ::b it is invalid.

seancorfield02:01:15

I expect that deep within the implementation of s/keys, the (unevaluated) symbols conj and my-keys are processed in such a way that they fail to be treated as keys at all. @kenny

seancorfield02:01:49

Ah, looking at the source of s/keys it filters out anything that isn't a keyword:

req-keys (filterv keyword? (flatten req))
        req-un-specs (filterv keyword? (flatten req-un))

kenny02:01:03

Then why is this validated correctly?

(def my-keys [::a])
=> #'boot.user/my-keys
(s/def ::a int?)
=> :boot.user/a
(s/valid? (s/keys :req (conj my-keys ::b))
          {::a "a"
           ::b "b"})
=> false

kenny02:01:27

(s/valid? (s/keys :req (conj my-keys ::b))
          {::a 1
           ::b "b"})
=> true

kenny02:01:42

I always forget the implicit checking of keys.

seancorfield02:01:30

It looks like s/keys tries to treat anything that isn't a keyword in the sequence supplied to :req or :req-un as some sort of predicate but I'm not quite sure I follow all the parts of the macro...

kenny02:01:24

Doesn't Spec auto-check any keys in a map against any specs defined for those keys?

kenny02:01:43

Which would explain why

{::a "a"
 ::b "b"}
is invalid

seancorfield02:01:29

Ah, I see what it's doing with those symbols:

:pred-forms (quote
               [(clojure.core/fn [%] (clojure.core/map? %))
                (clojure.core/fn [%] clojure.core/conj)
                (clojure.core/fn [%] my-keys)
                (clojure.core/fn
                 [%]
                 (clojure.core/contains? % :user/b))]),

seancorfield02:01:59

and when you evaluate clojure.core/conj and my-keys they evaluate to truthy values (not nil or false).

seancorfield02:01:28

@kenny Did you read the bit where I said (conj my-keys ::b) is not evaluated?

seancorfield02:01:03

It treats that as the key ::b and two predicates (which are "true").

seancorfield02:01:17

So your spec is equivalent to (s/keys :req [::b])

kenny02:01:30

Is that from a macroexpand?

seancorfield02:01:46

Yes, the above :pred-forms snippet is

kenny02:01:33

Gotcha. As always, everything has a logical explanation šŸ™‚

seancorfield02:01:54

Very logical. Not always obvious šŸ™‚

seancorfield02:01:58

OK, gotta run...

kenny02:01:26

Thanks. Have a good night!

seancorfield02:01:48

...back šŸ™‚

martinklepsch20:01:47

(s/def ::name string?)
(s/def ::ns string?)
(s/def ::doc (s/nilable string?))
(s/def ::src string?)
(s/def ::type {:var :fn :macro})
(s/def ::line int?)
(s/def ::column int?)

(s/def ::def
  (s/keys :req-un [::name ::ns ::doc ::src ::type
                   ::line ::column]))
Trying to sample this spec but getting StackOverflowError ā€” is there anything obvious Iā€™m doing wrong?

martinklepsch20:01:04

Never mind, I had two specs named ::ns šŸ™ˆ

conan21:01:22

I wrote a blog post about writing specs for URLs: http://conan.is/blogging/a-spec-for-urls-in-clojure.html

misha21:01:43

@martinklepsch also

(s/def ::type {:var :fn :macro}) ;;->
(s/def ::type #{:var :fn :macro})