Fork me on GitHub

How can I force spec to not validate a namespaced key against an optional spec in a map? I have a multispec with two clauses where the only difference is in the inclusion of a namespaced key, but because the key matches a spec the library will simply go ahead and validate that key against the spec even though it should not for that specific clause.

Alex Miller (Clojure team)12:10:22

keys will always validate namespaced keys

Alex Miller (Clojure team)12:10:32

That’s kind of central to the whole design of spec


It just feels inconsistent to how un-namespaced maps are treated - though I guess one could technically argue that if I'm saying a namespaced key should have a particular shape then it should have it whether or not it is required in a map. I have since tweaked it so that this is indeed the case, though given how namespacing is a particularly burdensome thing in our service (I have stripped it from everywhere except for one namespace, and even there it feels a bit redundant) this was quite the surprise in behaviour.


if you think about them (kinda sorta) like Types it makes sense. a :animal/lion is always the same thing.


Rich has a philosophical position that maps should be a bag of keys, and type information comes primarily from keys not from the bag


I think is inaccurate to characterise it as philosophical. Having consistent meaning associated with names in a program makes your program easier to reason about.


I found that in our specific use case, where we have lots of things that look the same up to a certain level and can diverge in some specifics, namespacing became a burden both during testing and during main program flow. Our use case might just be a special scenario, though I suspect I would probably do it the same way again if I was facing largely similar scenario - i.e., internal entities with roughly similar shapes, as I really don't like the idea of mixing different namespaces in a single map.


I hate working on programs where the thing I'm looking at shares a name with something else but is different. E.g. if "products" in one place referred to the ids of products you're allowed to see, but elsewhere referred to a list of product entities from the database.


Mixed namespaces don't create any bother for me, I believe that exists so you can have separate pieces of code which operates on the same entity as long as it fulfils the data contract. E.g. It's easier to understand code which operates on "Twitter account" which is a limited number of details, rather than "user" which is much larger and requires more juggling in your head.


Yeah - and I'd imagine that would work well if the maps truly mixed contexts. In our case, they mostly mix different datatypes that tend to look similar to one another, with a common "envelope" map around them. Given the benefits we gain out of being able to shove everything into a common processing pathway stripping the namespacing ended up making things a lot less unwieldy than it was.


Sounds like stamp coupling


When taken to an extreme, these ideas are still coupling. Functional connections are the only kind that's free from this, but that's a lot of typing and not much abstraction.


I don’t disagree it’s nice when keys = types and they are well namespaced and shared across entire application. I think Dominic took “philosophical” as a slur which I absolutely do not mean


I didn't think you were necessarily disparaging it, but I hear the term "philosophical" a lot when people feel a stance is without pragmatics and more rooted in perfectionism at cost to real software. I just wanted to attempt to explain the importance of this stance.


fair enough


yeah, I think it is slightly impractical, but aspirational


the impracticality for me is entirely centered around those two use cases: typing someone else’s data which doesn’t share that philosophy (which, honestly, is the vast majority of systems! most think property meanings should come from the record type they are in), or cases where the key is used “as a value” in e.g. a DSL instead of its “natural” meaning.


For me, that's why there is the split between unqualified and qualified keys: the former are "out there" and you have to assign meaning on a per-case basis; the latter should be unique keys within your domain and should have a single meaning for each name (in terms of the data type).


If you're a piece in a pipeline and you need to keep the data fairly untainted, I can appreciate that difficulty.


(and as an adjunct to my comment: if you can't assign a single meaning for a name "globally" then don't use a qualified keyword for it -- stick with unqualified keys that you have more flexibility with)


However, keys in fact do not always have the same type, e.g. a map key in a DSL vs a map key in data


e.g. datomic pull expression map vs the value of the pull


the “bag” sometimes imparts meaning to the keys, IOW


not to mention having to deal with other systems which don’t share your “maps are only keysets” philosophy, but I think the spec2 select machinery is an acknowledgement of that problem

Alex Miller (Clojure team)18:10:27

spec 2 select just says that the notion of required/optional keys is contextual

Alex Miller (Clojure team)18:10:40

it still maintains strong semantics for attributes across aggregates


but it also has contextual key renaming, no?

Alex Miller (Clojure team)18:10:43

it has the ability to do binding of un-qualified keys to specs, but un-qualified keys only


that’s all I meant


{:first} for example


I think schema is what I am talking about, as being able to impart some meaning to the aggregate


instead of being “merely” a keyset

Alex Miller (Clojure team)18:10:22

the intent here is not that attributes can be assigned many different meanings, but that unqualified keys are missing the link to attribute semantics

Alex Miller (Clojure team)18:10:53

it's still just a collection of attributes though


yes it does not solve that problem