Fork me on GitHub

I guess there’s no way to override that? The issue I’m running into is that merge assumes the key specs it gets are open for extension; so keys-excl merged with anything else fails to produce anything, because the keys-excl part of the spec that checks that the map only contains specific keys will fail on all the keys being added by anything that’s merged in


@alexmiller: last call in my clojure function is java (interop) fn call which returns void. So in clojure if java fn returns void we see nil, if i understand correctly.


@lvh: Instead of merge, have you tried to make an intersection operation?


Sometimes the intersection of two keys-excl won't work either of course, since you have certain mandatory keys.


"closed" key sets seems like a common request, hopefully Rich reconsiders his choice here. I get that he doesn't want to make it the default, but simply providing a way to spec this can't hurt

Alex Miller (Clojure team)13:07:22

There are no plans to revisit that afaik

Alex Miller (Clojure team)13:07:58

@lvh you might try going down the path of treating your map as a coll-of tuple entries instead (make sure to use :into {})

Alex Miller (Clojure team)13:07:40

Then merge that with s/keys to pick up the attribute checks


It's super easy to please everybody with a system with "closed" keysets by default (you can just add a k/v spec with any? if you want to "open" the map), but that apparently is out of the question. I personally do not understand the logic here. Never saw anybody complain about the "strictness" of prismatic/Schema for these things for instance.


then again, I don't consider the openness of records a good thing either


@xcthulhu: I’m not sure I understand. As in, remove keys?


I mean, I guess I could, but that seems very gauche. What would that look like? I have a base spec that everything conforms to, and some stuff that’s specific per type (i.e. multi-spec)


@lvh:Yeah, you would remove keys. If a map-spec isn't open under extension, it may be open under specialization.


So provided that your map-specs s1 and s2 that have the form (keys-excl :opt [...] :opt-un [...]) then you could have

(defn intersection
  [s1 s2]
  (spec/with-gen (spec/and s1 s2)
    #(fmap (fn [[a b]] (select-keys a (keys b)))
           (spec/gen (spec/tuple s1 s2)))))


And you'd have the rule:

(valid? (intersection s1 s2) m)
; if and only if
(and (valid? s1 m) (valid? s2 m))


@mpenet: search slack history for @richhickey expounding on closed keysets making brittle systems


It's kind of in the same class as static vs dynamic lang/systems imho: it depends, sometimes exactitude is necessary. My point was that allowing both would do no harm, it would be optional.


I guess that ship has sailed anyway, and likely not open for debate as usual.


that's a pessimistic attitude


it's not open for debate (IMHO) because it's a bad idea, and the core team can justify why


im guessing no one went back to the original discussion. This comes up often enough that it's probably worth documenting why it's not a good idea


That’s a pretty good discussion, and includes a not-too-bad way to accomplish it if you really feel you must.