Spec can only say what is included in a map, it can't exclude. And the spec is late-bound by the caller. Different callers mandate different specs for the same entity. I see that you know this but I object to the wording "constrained".
(ns user
(:use clojure.set)
(:require [clojure.spec.alpha :as s]))
(s/def ::map-closed #(-> %
keys
set
(difference #{:a :b :c})
empty?))
(s/valid? ::map-closed {:a 1, :c 2})
=> true
(s/valid? ::map-closed {:a 1, :c 2, :e 3})
=> falseThe way we've used it, it worked almost as if the maps were statically-typed, and it saved my bacon quite a few times.