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, [email protected] 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 <[email protected]>
>> 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:* [email protected] <[email protected]> on behalf
>>>>>> of Beau Fabry <[email protected]>
>>>>>> *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 <[email protected]> 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 [email protected]
>>>>>> Note that posts from new members are moderated - please be patient
>>>>>> with your first post.
>>>>>> To unsubscribe from this group, send email to
>>>>>> [email protected]
>>>>>> 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 [email protected].
>>>>>> 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 [email protected]
>>> Note that posts from new members are moderated - please be patient with
>>> your first post.
>>> To unsubscribe from this group, send email to
>>> [email protected]
>>> 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 [email protected].
>>> 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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
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 [email protected].
For more options, visit https://groups.google.com/d/optout.