apologies if this is documented somewhere, but i was recently downgrading from spec2 to spec1, and was surprised to find that s/keys
in spec1 will validate registry keys that are present in the value, but not present in the spec:
(require '[clojure.spec.alpha :as s)
(s/def ::a int?)
(s/def ::b even?)
(s/def ::c odd?)
(s/def ::ks1
(s/keys :req-un [::a ::b])) ; NOTE: no mention of `::c`
(s/explain ::ks1 {:a 1 :b 2 ::c 4})
; => 4 - failed: odd? in: [:user/c] at: [:user/c] spec: :user/c
i tried using the latest version of spec1, and it seemed to still work this way. is this the intent? it seems like that runs contrary to the idea of openness that spec promotes. design-wise, it's almost certainly a bad idea to be using qualified keys that dont conform to their spec, but strictly from the spec perspective this seems counterintuitive.
i'm curious if others have run up against this or if it's a rare case in practice
I believe this is by design. spec is for defining legal values for particular keys, no matter which maps they might appear in. I think that it is in support of openness of specs -- you can include any key in any map if you want.
i think i agree—that once you specify the set of legal values for a qualified key, any instance of that key (in any map) should always have a value that conforms to the specification, no matter where it is in your program. so design-wise, i'm completely with you. where i get hung up is mainly around the presence of the :req
and similar options of the s/keys
form, if the s/keys
op is going to validate every key that exists in the registry regardless of the keys you provide for those options
if true, what purpose does s/keys
provide? Seems like it's supposed to allow you to narrow your focus of key values that you want verified
and if it's also by design, why did spec2 change it?
that's why i ask about the intent versus the way its currently working—it may be a bug in spec2, for example
fair. definitely not saying one way is right over another, just want to understand for sure
this is doc'ed in the s/keys docstring and should (afaik) be the same in spec 1 / 2
ah, i see now! apologies to have missed that
there have been some changes around kw spec references in spec 2 and its possible something was broken there
yep, that makes sense. i just wanted to get a bit more clarity on things given the difference i was seeing. appreciate all the help @andy.fingerhut @alexmiller 🙏
apologies to dredge this back up, but i'm looking at my program and realizing that i'm redundantly validating values because s/keys
works this way. is there any way to avoid this? is there a recommended way to validate a subset of the keys in a map? it doesn't seem possible with spec1 without using select-keys
or otherwise filtering the map first
@nolan If you are using qualified keys, this is documented behavior and is intentional. Qualified keys are supposed to be “globally unique” and therefore the Specs declared for them should also be globally applicable. That’s by design.
In Spec 2, select
is about requiredness, they’ll still be validated if they are present.
absolutely! i agree completely with the design ethos of global uniqueness of the specification of qualified keys. it's really a matter of difficult-to-detect redundancy in the validation. for example, if i do an expensive validation using s/keys
upfront, then compute with that map, potentially adding keys to it, and later i want validate the new keys—`s/keys` is a deceptively dangerous choice for that use-case. now that i know this is the case, i can certainly rework things to account for the multiple validations, but it seems like it would be a common use-case to simply validate only certain keys in a map. or maybe i'm just not thinking about it the right way!
The latter, I think.
If you use unqualified keys, you only get validation of what is in s/keys
— because they’re considered to have a “local context”.
i mainly find myself running up against it when trying to avoid unnecessary work. luckily we've got all kinds of scissors and scalpels for chopping these maps up in order to validate subsets, so that's i think what i'll need to do
thanks for all the help and direction!
Well, the Spec validation is all about the key names. s/keys
is more about requiredness and generation.
got it. that's helpful. i'm going to approach this through that lens and see what comes out. thanks again @seancorfield!