> > One way to prevent that would be to only ever use aliases for fully > qualified keywords, never type out the whole thing. >
Uh, you forced me to go learn more about keywords. I didn't know you could do ::namespace/name to alias keywords. I've been manually namespacing my keywords until now, which was tedious and error prone. Also, because I was doing it manually, I made my namespace real short, as its annoying to have to type :com.company.project.wtv/name everywhere. Thanks for the tip. I do feel that for this though, it asks for a bit too much discipline on the programmer, and in this case, also a good amount of knowledge around spec and keywords, so I might still use the macro, but also switch to aliased keywords. On Friday, 10 November 2017 10:07:52 UTC-8, Beau Fabry wrote: > > One way to prevent that would be to only ever use aliases for fully > qualified keywords, never type out the whole thing. > > On Thursday, November 9, 2017 at 11:22:24 PM UTC-8, Didier wrote: >> >> I just stumbled upon another potential mistake here. When you have specs >> split across namespaces. Its possible for a map spec in one namespace to >> have one of its key's use a spec from another namespace. If you forget to >> require that other namespace though, you won't know, and your map will >> always validate. >> >> (s/def ::spec (s/keys :req [:other/spec])) >> >> If :other/spec is not registered, ::spec still will succeed at being >> registered. And then, assuming :other/spec is defined as: >> >> (s/def :other/spec int?) >> >> I wouldn't matter, since: >> >> (s/valid? ::spec {:other/spec "123"}) >> >> Will return true. >> >> But if you register :other/spec, it would return false. >> >> Normally, this has not been an issue for me, but now that I share my >> specs more, I've got specs in different namespace using one another, and >> I've already made this mistakes a few time, causing validation I thought >> was there to protect me, to actually be missing. >> >> So I made my own keys macro: >> >> (defmacro known-keys >> [& {:keys [req req-un opt opt-un gen] :as args}] >> (letfn [(known-spec? [k] (boolean (s/get-spec k)))] >> (doseq [e (concat req req-un opt opt-un)] >> (when (not (known-spec? e)) >> (throw (ex-info (str e " is not a currently registered spec.") >> args))))) >> `(s/keys ~@(interleave (keys args) (vals args)))) >> >> Which first checks that all keys are currently registered, and if so, it >> delegates back to s/keys. Otherwise it throws an exception at macro >> expansion time. >> >> I think this would also solve OPs problem, since it would throw if typos >> are made also. >> >> On Saturday, 14 October 2017 04:45:47 UTC-7, stuart....@gmail.com wrote: >>> >>> Hi Leon, >>> >>> I think it would be a mistake to introduce temporal coupling to prevent >>> typos. Here is an alternative that lets you identify "missing" keys specs at >>> the time and place of your choosing, and then handle them as you deem >>> appropriate, without imposing those decisions on other users of spec: >>> >>> https://gist.github.com/stuarthalloway/f4c4297d344651c99827769e1c3d34e9 >>> >>> Regards, >>> Stu >>> >>> >>> >>> >>> On Tue, Oct 10, 2017 at 12:33 PM, Leon Grapenthin <grapent...@gmail.com> >>> wrote: >>> >>>> In terms of code loading, acyclic dependencies turned out to be a great >>>> design choice in Clojure - why its benefits shouldn't apply to or be >>>> justified for spec loading is totally unclear to me. >>>> >>>> To make my point more clear let me recap. I am simply asking for s/keys >>>> to throw if provided specs aren't registered. Because my colleagues and I >>>> myself made costly mistakes that would have been prevented. The most >>>> common >>>> scenario is a typo like the one I have illustrated above. >>>> >>>> I have asked what benefits justify current behavior? >>>> >>>> The only justification comes from Sean saying that it helps him >>>> prototyping. While I agree I also observe that this is simultaneously the >>>> trapdoor leading to such silently passing specs. And why prototyping needs >>>> should not be a primary concern in how s/keys behaves. >>>> >>>> I have tried to make a case for current behavior: It allows to say a >>>> key is there, without saying anything about its value. I have pointed out >>>> (s. a.) why this IMO has too little utility to justify anything. >>>> >>>> Regarding Clojure being a dynamic lanugage this doesn't really make a >>>> difference here: There is not much dynamic going on about registration and >>>> spec in general. Registration etc. is evaluated at compile time. Note >>>> that >>>> s/def, s/keys etc. are all macros whose expansion is evaluated at compile >>>> time. >>>> >>>> On Monday, October 9, 2017 at 7:20:42 PM UTC+2, Beau Fabry wrote: >>>>> >>>>> > The argument that existence of specs provided to s/keys can only be >>>>> checked at runtime is false. >>>>> >>>>> > The argument that that recursive specs are impossible if existence >>>>> of specs provided to s/keys was checked at compile time is also false. >>>>> >>>>> Could you explain to us why this is false? Clojure is a dynamic >>>>> language, as such I don't see how you could define a time when all specs >>>>> need to be present. How would I enter this spec at the repl if spec >>>>> definition was required at s/keys invocation time? >>>>> >>>> >>>> >>>>> >>>>> On Friday, October 6, 2017 at 4:32:41 PM UTC-7, Leon Grapenthin wrote: >>>>>> >>>>>> The argument that existence of specs provided to s/keys can only be >>>>>> checked at runtime is false. >>>>>> >>>>>> The argument that that recursive specs are impossible if existence of >>>>>> specs provided to s/keys was checked at compile time is also false. >>>>>> >>>>>> The usecase for libraries is not convincing: If the libraries author >>>>>> states "the map has to have a key K" nobody can spec K further since >>>>>> that >>>>>> would be a race condition among consumers (who s/defs K first?). >>>>>> Requiring >>>>>> the libraries author to declare K as any? would at least require him to >>>>>> decide and convey his intent. >>>>>> >>>>>> The argument that not checking a value associated with a key is >>>>>> corresponding to a guding design principle of map specs being based on a >>>>>> keyset is not stating enough to justify discussed behavior. The utility >>>>>> of >>>>>> knowing that a keyset is present is close to none, which should be the >>>>>> main >>>>>> reasons why s/keys validates values. Again: Saying "A map that has a key >>>>>> called ::foo" is pretty pointless in Clojure. If every map in every >>>>>> Clojure >>>>>> program I wrote had a key ::foo they would all produce the exact same >>>>>> results as if they didn't and I bet yours would, too. >>>>>> >>>>>> Prototyping is indeed a bit more easy if one does not have to to >>>>>> declare every spec used in a s/keys. However, that is particularly >>>>>> damning >>>>>> if you forget to add that spec later or mistype its name when doing so. >>>>>> Which happens, and which is why I'm unhappy with this design letting >>>>>> such >>>>>> typical human errors pass compilation. It would also help my prototyping >>>>>> needs if I could reference symbols that are not declared, but I prefer >>>>>> the >>>>>> compiler errors before going live. >>>>>> >>>>>> On Saturday, October 7, 2017 at 12:01:34 AM UTC+2, Sean Corfield >>>>>> wrote: >>>>>>> >>>>>>> As one of the (apparently pretty uncommon) users who actually does >>>>>>> happily define s/keys specs without correspondingly speccing the leaves >>>>>>> as >>>>>>> an "incrementally lock down/validate" approach, I wouldn't be too upset >>>>>>> if >>>>>>> I lost that ability and it started throwing an error. I mean it throws >>>>>>> an >>>>>>> error if I go to generate it anyway. >>>>>>> >>>>>>> >>>>>>> >>>>>>> **puts hand up!** >>>>>>> >>>>>>> >>>>>>> >>>>>>> I don’t want to have to write (s/def ::some-key any?) all over the >>>>>>> place as I’m developing specs, just to satisfy an overly eager checker >>>>>>> (in >>>>>>> my mind). Worse, since the check would need to be deferred until >>>>>>> validation >>>>>>> time, as Beau notes, the omission of an “any?” key spec might not even >>>>>>> show >>>>>>> up until much further down the line. >>>>>>> >>>>>>> >>>>>>> >>>>>>> To me, this default behavior of silently not checking the _*value*_ >>>>>>> associated with a _*key*_ is in keeping with the design principles >>>>>>> of spec which focus on maps being based on a *key set*, while >>>>>>> offering functions to allow you to optionally check values. >>>>>>> >>>>>>> >>>>>>> >>>>>>> Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN >>>>>>> An Architect's View -- http://corfield.org/ >>>>>>> >>>>>>> "If you're not annoying somebody, you're not really alive." >>>>>>> -- Margaret Atwood >>>>>>> >>>>>>> >>>>>>> ------------------------------ >>>>>>> *From:* clo...@googlegroups.com <clo...@googlegroups.com> on behalf >>>>>>> of Beau Fabry <imf...@gmail.com> >>>>>>> *Sent:* Friday, October 6, 2017 9:10:36 AM >>>>>>> *To:* Clojure >>>>>>> *Subject:* Re: [core.spec] Stricter map validations? >>>>>>> >>>>>>> A use case that comes to mind is a system/library that specifies the >>>>>>> structure of some inputs/outputs, but lets users/consumers (optionally) >>>>>>> specify further validation of the leaves. I suppose that would be >>>>>>> possible >>>>>>> with (s/def ::foo any?) but you'd have to be a bit more careful about >>>>>>> load >>>>>>> order. The other use case (which is mine) is I'm just lazy and only >>>>>>> want to >>>>>>> write out broad strokes specs sometimes without getting into the nitty >>>>>>> gritty. >>>>>>> >>>>>>> If s/keys were to validate that the keys it's provided have specs it >>>>>>> would have to do it at validation time, so you wouldn't get the error >>>>>>> until >>>>>>> something was actually validated against that key spec. Trying to do it >>>>>>> at >>>>>>> definition time would break recursive specs. >>>>>>> >>>>>>> As one of the (apparently pretty uncommon) users who actually does >>>>>>> happily define s/keys specs without correspondingly speccing the leaves >>>>>>> as >>>>>>> an "incrementally lock down/validate" approach, I wouldn't be too upset >>>>>>> if >>>>>>> I lost that ability and it started throwing an error. I mean it throws >>>>>>> an >>>>>>> error if I go to generate it anyway. >>>>>>> >>>>>>> On Friday, October 6, 2017 at 8:58:38 AM UTC-7, Leon Grapenthin >>>>>>> wrote: >>>>>>>> >>>>>>>> Thanks, Beau. >>>>>>>> >>>>>>>> I am still interested why this default behavior has been chosen. It >>>>>>>> doesn't seem like a reasonable trade-off at this point. >>>>>>>> >>>>>>>> It enables me to say: "The map must have this key", without >>>>>>>> specifying how the data mapped to it will look like. >>>>>>>> >>>>>>>> If I ever wanted to do that, I could as well spec that key with >>>>>>>> "any?". >>>>>>>> >>>>>>>> What are other benefits? They must justify the expense of likely >>>>>>>> runtime errors. >>>>>>>> >>>>>>>> >>>>>>>> On Friday, October 6, 2017 at 5:34:16 PM UTC+2, Beau Fabry wrote: >>>>>>>>> >>>>>>>>> Leon, perhaps you could add this code to your test suite? >>>>>>>>> >>>>>>>>> boot.user=> (let [kws (atom #{})] >>>>>>>>> #_=> (clojure.walk/postwalk (fn [x] (when >>>>>>>>> (qualified-keyword? x) (swap! kws conj x)) x) (map s/form (vals >>>>>>>>> (s/registry)))) (clojure.set/difference @kws (set (keys >>>>>>>>> (s/registry)))) >>>>>>>>> #_=> ) >>>>>>>>> #{:clojure.spec.alpha/v :clojure.spec.alpha/k} >>>>>>>>> boot.user=> >>>>>>>>> >>>>>>>>> On Friday, October 6, 2017 at 5:56:29 AM UTC-7, Leon Grapenthin >>>>>>>>> wrote: >>>>>>>>>> >>>>>>>>>> Open maps/specs are fine. >>>>>>>>>> >>>>>>>>>> s/keys supporting unregistered specs are not. >>>>>>>>>> >>>>>>>>>> At least to me. I just fixed two more bugs in production that >>>>>>>>>> were would not have happened. >>>>>>>>>> >>>>>>>>>> What are the supposed benefits of this feature? >>>>>>>>>> >>>>>>>>>> I can only infer "being able to require keys without their spec >>>>>>>>>> being known" which is a usecase I had exactly 0.00% of the time so >>>>>>>>>> far. >>>>>>>>>> >>>>>>>>>> Anything I have missed? >>>>>>>>>> >>>>>>>>>> Kind regards, >>>>>>>>>> Leon. >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> On Wednesday, October 4, 2017 at 7:05:29 PM UTC+2, Beau Fabry >>>>>>>>>> wrote: >>>>>>>>>>> >>>>>>>>>>> Seems like that's the reasonable place to check it, otherwise >>>>>>>>>>> you're forced into an ordering for your specs and cannot write >>>>>>>>>>> recursive >>>>>>>>>>> strict map specs. >>>>>>>>>>> >>>>>>>>>>> On Wednesday, October 4, 2017 at 8:59:59 AM UTC-7, Yuri >>>>>>>>>>> Govorushchenko wrote: >>>>>>>>>>>> >>>>>>>>>>>> Thanks. This approach is also different from the macro because >>>>>>>>>>>> it will check specs existence at the validation time, not at the >>>>>>>>>>>> s/def call. >>>>>>>>>>>> >>>>>>>>>>>> On Wednesday, October 4, 2017 at 4:18:16 PM UTC+3, Moritz >>>>>>>>>>>> Ulrich wrote: >>>>>>>>>>>>> >>>>>>>>>>>>> Yuri Govorushchenko <yuri....@gmail.com> writes: >>>>>>>>>>>>> >>>>>>>>>>>>> > Thank you the pointers! So far I ended up with writing a >>>>>>>>>>>>> small `map` macro >>>>>>>>>>>>> > which is similar to `s/keys` but checks that keys are >>>>>>>>>>>>> already in the >>>>>>>>>>>>> > registry: >>>>>>>>>>>>> https://gist.github.com/metametadata/5f600e20e0e9b0ce6bce146c6db429e2 >>>>>>>>>>>>> >>>>>>>>>>>>> >>>>>>>>>>>>> Note that you can simply combine a custom predicate and >>>>>>>>>>>>> `s/keys` in >>>>>>>>>>>>> clojure.spec to verify that all keys in a given map have a >>>>>>>>>>>>> underlying >>>>>>>>>>>>> spec: >>>>>>>>>>>>> >>>>>>>>>>>>> ``` >>>>>>>>>>>>> (s/def ::whatever (s/and (s/keys ...) >>>>>>>>>>>>> #(every? keyword? (keys %)) >>>>>>>>>>>>> #(every? (comp boolean s/get-spec) >>>>>>>>>>>>> (keys %)) ) >>>>>>>>>>>>> ``` >>>>>>>>>>>>> >>>>>>>>>>>> -- >>>>>>> You received this message because you are subscribed to the Google >>>>>>> Groups "Clojure" group. >>>>>>> To post to this group, send email to clo...@googlegroups.com >>>>>>> Note that posts from new members are moderated - please be patient >>>>>>> with your first post. >>>>>>> To unsubscribe from this group, send email to >>>>>>> clojure+u...@googlegroups.com >>>>>>> For more options, visit this group at >>>>>>> http://groups.google.com/group/clojure?hl=en >>>>>>> --- >>>>>>> You received this message because you are subscribed to the Google >>>>>>> Groups "Clojure" group. >>>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>>> send an email to clojure+u...@googlegroups.com. >>>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>>> >>>>>> -- >>>> You received this message because you are subscribed to the Google >>>> Groups "Clojure" group. >>>> To post to this group, send email to clo...@googlegroups.com >>>> Note that posts from new members are moderated - please be patient with >>>> your first post. >>>> To unsubscribe from this group, send email to >>>> clojure+u...@googlegroups.com >>>> For more options, visit this group at >>>> http://groups.google.com/group/clojure?hl=en >>>> --- >>>> You received this message because you are subscribed to the Google >>>> Groups "Clojure" group. >>>> To unsubscribe from this group and stop receiving emails from it, send >>>> an email to clojure+u...@googlegroups.com. >>>> For more options, visit https://groups.google.com/d/optout. >>>> >>> >>> -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.