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 <grapenthinl...@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/metame
>>>>>>>>>> tadata/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 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.
>

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

Reply via email to