>From my perspective, there are two specs.  I'm not trying to mash them
together, however I would like to use the appropriate spec with the same
keyword in maps, in different contexts.

It wouldn't be an issue if the contexts were in separate namespaces.  It's
only an issue because the two specs are in the same namespace, and I want
to use them in different places with the same map key.

(Or so it seems to me, still wrapping my head around spec).

On Tue, Feb 7, 2017 at 11:10 AM, Alex Miller <a...@puredanger.com> wrote:

> Spec names are intended to have enduring global semantics. So the notion
> of the same spec name having different semantics at different times seems
> to be at odds with that.
>
> In general, it's often helpful to think about all the possible values that
> an attribute will have - that's the true spec. In this case, it seems like
> what you have for ::coercible-job-type is the true spec - it can be either
> a string or other named value.
>
> And then think about the places where you need to add additional
> constraints to your ::job-record spec to narrow it. Like maybe here:
>
> (s/fdef ... :ret (s/and ::job-record #(-> % ::job-type string?)))
>
> You're on the brink of suggesting subtypes and covariant / contravariants,
> etc and that's a path spec is not going down.
>
>
> On Tuesday, February 7, 2017 at 9:40:24 AM UTC-6, Dave Tenny wrote:
>>
>> Let's say I have these definitions for a "job" record I'm managing,
>> perhaps in a database.
>>
>> (s/def ::job-status #{:in-progress :completed})
>> (s/def ::user-id (s/and integer? #(>= % 0)))
>> (s/def ::job-id integer?)
>>
>> (s/def ::coercible-job-type (s/and named? #(not (empty? (name %)))))
>> (s/def ::job-type (s/and string? #(not (empty? %))))
>>
>> So we have a job status which can be any of a limited set of keywords,
>> integer user and job ID's.
>>
>> Then there's job type and my issue.  Depending on the context I want
>> either to insist that job type will be purely a non-empty string, or that
>> it may also be a 'named?' object,
>> i.e. something for which the clojure.core/name function will work.
>>
>> If I'm just returning a pure job record, my definition would look like
>> this:
>>
>> (s/def ::job-record
>>   (s/keys :req-un [::job-id ::user-id ::job-type ::job-status]))
>>
>> So a map with all those keys and type definitions. And that's great.  I
>> define functions that return these
>> maps and so there's an (s/fdef ... :ret ::job-record).
>>
>> Now let's say I have an update! function that can take a map of optional
>> fields and update the record in the database with the fields that were
>> specified.
>>
>> (s/def ::field-options (s/keys :opt-un [::job-id ::user-id ::job-type
>> ::job-status]))
>> (s/fdef update! :args (s/cat ::field-map ::field-options) ...)
>> (defn update! [field-map] ... (:job-type field-map) ...)
>>
>> So far, so good. I have a parameter 'field-map' that can take optional
>> map keys, validate map arguments if the testing instrumentation is enabled,
>> etc.
>>
>> Here is my problem.  There appears to be no way for me to define, in this
>> namespace, map specs with a :job-type key, but that will have
>> :coercible-job-type semantics,
>> for my update! function. Or at least I don't see an easy way short of
>> writing a function to do everything that the existing machinery is doing
>> with respect to key and value validation,
>> which is a lot of work to handle this tiny thing, basically the ability
>> to have map keys and spec semantics bound to different names in s/keys
>> specs.
>>
>> For example, the following pseudo-spec might let me define a map key
>> :job-type whose semantics were specified by the ::coercible-job-type
>> definition.
>> Or inlining of definitions which the spec guide says was omitted on
>> purpose.
>>
>> (s/def ::field-map (s/keys :opt-un [... [::coercible-job-type :as ::job-
>> type]]))
>>
>> I want the map key to be ":job-type", but the clojure.spec semantics for
>> that field, *in selective module definitions*, to have ::coercible-job-type
>> behavior.
>>
>> So... am I missing something?
>>
>> --
> 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 a topic in the
> Google Groups "Clojure" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/
> topic/clojure/ioR4875rRxU/unsubscribe.
> To unsubscribe from this group and all its topics, 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