clojure-spec

About: http://clojure.org/about/spec Guide: http://clojure.org/guides/spec API: https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html
Nolan 2021-05-11T19:07:55.142Z

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

Nolan 2021-05-11T19:10:51.144900Z

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.

Nolan 2021-05-11T19:11:35.145500Z

i'm curious if others have run up against this or if it's a rare case in practice

2021-05-11T19:19:13.146500Z

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.

Nolan 2021-05-11T19:22:09.149800Z

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

localshred 2021-05-11T19:22:09.149900Z

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

localshred 2021-05-11T19:22:51.150300Z

and if it's also by design, why did spec2 change it?

Nolan 2021-05-11T19:24:19.151400Z

that's why i ask about the intent versus the way its currently working—it may be a bug in spec2, for example

localshred 2021-05-11T19:24:53.151900Z

fair. definitely not saying one way is right over another, just want to understand for sure

alexmiller 2021-05-11T19:36:28.153600Z

this is doc'ed in the s/keys docstring and should (afaik) be the same in spec 1 / 2

👍 1
🙏 1
Nolan 2021-05-11T19:37:10.154800Z

ah, i see now! apologies to have missed that

alexmiller 2021-05-11T19:37:21.155Z

there have been some changes around kw spec references in spec 2 and its possible something was broken there

Nolan 2021-05-11T19:38:23.156Z

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 🙏

👍 1
Nolan 2021-05-11T21:32:24.165400Z

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

seancorfield 2021-05-11T21:38:42.166800Z

@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.

seancorfield 2021-05-11T21:39:57.168100Z

In Spec 2, select is about requiredness, they’ll still be validated if they are present.

Nolan 2021-05-11T21:45:32.173300Z

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!

seancorfield 2021-05-11T21:49:13.176Z

The latter, I think.

seancorfield 2021-05-11T21:49:54.177Z

If you use unqualified keys, you only get validation of what is in s/keys — because they’re considered to have a “local context”.

👍 1
🙏 1
Nolan 2021-05-11T21:50:34.177300Z

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

Nolan 2021-05-11T21:51:41.178500Z

thanks for all the help and direction!

seancorfield 2021-05-11T21:51:45.178600Z

Well, the Spec validation is all about the key names. s/keys is more about requiredness and generation.

Nolan 2021-05-11T21:53:43.179300Z

got it. that's helpful. i'm going to approach this through that lens and see what comes out. thanks again @seancorfield!