Your spec names need different namespaces, but your functions do not:

(s/def :catalog-a/type #{"a" "b" "c"})
(s/def :catalog-a/ref string?)

(s/def :catalog-b/type #{:x :y :z})
(s/def :catalog-b/ref number?)


Then you need to say that the keys are unqualified when they appear in the 
map for this function:


(s/fdef get-product-from-catalog-a
 :args (s/keys :req-un [:catalog-a/type :catalog-a/ref]
 :ret    any?)

(s/fdef get-product-from-catalog-b
 :args (s/keys :req-un [:catalog-b/type :catalog-b/ref]
 :ret    any?)

In general clojure spec tighly binds key names in maps to spec names and it 
is very hard to separate them. This is a formal part of its rationale, 
expressed as "Map specs should be of keysets only" 
<http://clojure.org/about/spec#_map_specs_should_be_of_keysets_only>.

There is a case I do not understand how to do well, where I want a specific 
function to accept a map with a specific specced key, but I want that key's 
value to conform to some strict subset of possible values as well. In 
essence, I want to "subclass" the key.

The options I see are:

   1.  Make a different spec based on the parent spec but adding 
   refinements, then make and spec adaptor functions which do nothing but 
   change the key (and validate). I.e., do the respec dance before the code.
   2. In the body of the function, call another function that accepts the 
   spec as a value (rather than in a map), and refine the value there. Your 
   outer function will still have to accept other values and handle them 
   somehow (signal error, throw, whatever).

Both of these are awkward when dealing with existing systems which to not 
consider specs to be of keysets only. In particular, we deal with a 
specification called FHIR <https://www.hl7.org/fhir/> whose type model 
allows "profiles", which are declared on an instance (e.g. a map would have 
a type key for the base type and optionally a "profiles" key which is any 
additional specs to which the map must conform). A profile is essentially a 
restriction of the instance's basic types (they can never extend) such that 
any profiled instance must always conform to the base type's spec. E.g. if 
a field is allowed to have values "a" and "b", the profile may say that 
field may only have "a". So in essence we want maps to potentially conform 
to multiple specs at once: the base spec which is the keys in the map, and 
a profile spec which uses the same keys but may add additional 
restrictions. I have not thought of a good way to express this in 
clojure.spec.

On Thursday, November 17, 2016 at 10:54:18 AM UTC-6, damien....@gmail.com 
wrote:
>
> I forgot the :req-un.
>
> (s/fdef get-product-from-catalog-[a|b]
>   :args (s/keys :req-un [::type ::ref])
>   :ret any?)
>
>
>
>

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