| This is a very good idea. And the reason I say that is because in our code base we have developed something similar, feeling the need for it :-). Our stuff is half-baked, though and I never got around to releasing it. What I'd like to see is something similar to what we did — checking more than just types. Here's an example from our code: (conforms example-object [map :from string? :to [map :required-keys {"products" [seq string?] "type" [seq #{"type1" "type2"}]}]]) This describes a map containing mappings from strings to maps of a certain kind (where "products" and "type" are required, "products" must map to a sequence of strings and "type" must map to a sequence of strings from a specific set). From our "conforms" docstring: Returns true if value conforms to typespec.
typespec can be either:
- a string, which value must match literally;
- a predicate, which value must satisfy;
- [seq sub-typespec], which means that value must be a
collection of elements, each of which must conform to
sub-typespec;
- [map specifiers...], where optional specifiers may include:
:from sub-typespec -- keys in the map must match sub-typespec;
:to sub-typespec -- likewise for values;
:required-keys [key1 typespec1 key2 typespec2...] -- map must
include the required keys and their values must match the
typespecs. We don't use that for function contracts and it might be overkill for that, but I'm pretty certain I'd like to be able to specify more than just types for map keys. A list of possible values would be tremendously useful. This could provide you with some ideas. I can of course contribute the "conforms" function (although it isn't that difficult to write). |
Actually, schema can express arbitrary constraints. Your example translates to schema as: