Skip to content

Commit de6a2b5

Browse files
committed
perf tweaks (resolve, or et al)
1 parent 99ab306 commit de6a2b5

File tree

1 file changed

+66
-36
lines changed

1 file changed

+66
-36
lines changed

src/clj/clojure/spec.clj

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -44,28 +44,21 @@
4444

4545
(defonce ^:private registry-ref (atom {}))
4646

47-
(defn- named? [x] (instance? clojure.lang.Named x))
48-
49-
(defn- with-name [spec name]
50-
(with-meta spec (assoc (meta spec) ::name name)))
51-
52-
(defn- spec-name [spec]
53-
(cond
54-
(keyword? spec) spec
55-
56-
(instance? clojure.lang.IObj spec)
57-
(-> (meta spec) ::name)))
47+
(defn- deep-resolve [reg k]
48+
(loop [spec k]
49+
(if (ident? spec)
50+
(recur (get reg spec))
51+
spec)))
5852

5953
(defn- reg-resolve
60-
"returns the spec/regex at end of alias chain starting with k, nil if not found, k if k not Named"
54+
"returns the spec/regex at end of alias chain starting with k, nil if not found, k if k not ident"
6155
[k]
62-
(if (named? k)
63-
(let [reg @registry-ref]
64-
(loop [spec k]
65-
(if (named? spec)
66-
(recur (get reg spec))
67-
(when spec
68-
(with-name spec k)))))
56+
(if (ident? k)
57+
(let [reg @registry-ref
58+
spec (get reg k)]
59+
(if-not (ident? spec)
60+
spec
61+
(deep-resolve reg spec)))
6962
k))
7063

7164
(defn- reg-resolve!
@@ -86,15 +79,32 @@
8679
[x]
8780
(c/and (::op x) x))
8881

82+
(defn- with-name [spec name]
83+
(cond
84+
(ident? spec) spec
85+
(regex? spec) (assoc spec ::name name)
86+
87+
(instance? clojure.lang.IObj spec)
88+
(with-meta spec (assoc (meta spec) ::name name))))
89+
90+
(defn- spec-name [spec]
91+
(cond
92+
(ident? spec) spec
93+
94+
(regex? spec) (::name spec)
95+
96+
(instance? clojure.lang.IObj spec)
97+
(-> (meta spec) ::name)))
98+
8999
(declare spec-impl)
90100
(declare regex-spec-impl)
91101

92102
(defn- maybe-spec
93103
"spec-or-k must be a spec, regex or resolvable kw/sym, else returns nil."
94104
[spec-or-k]
95-
(let [s (c/or (spec? spec-or-k)
105+
(let [s (c/or (c/and (ident? spec-or-k) (reg-resolve spec-or-k))
106+
(spec? spec-or-k)
96107
(regex? spec-or-k)
97-
(c/and (named? spec-or-k) (reg-resolve spec-or-k))
98108
nil)]
99109
(if (regex? s)
100110
(with-name (regex-spec-impl s nil) (spec-name s))
@@ -104,11 +114,27 @@
104114
"spec-or-k must be a spec, regex or kw/sym, else returns nil. Throws if unresolvable kw/sym"
105115
[spec-or-k]
106116
(c/or (maybe-spec spec-or-k)
107-
(when (named? spec-or-k)
117+
(when (ident? spec-or-k)
108118
(throw (Exception. (str "Unable to resolve spec: " spec-or-k))))))
109119

120+
(defprotocol Specize
121+
(specize* [_]))
122+
123+
(extend-protocol Specize
124+
clojure.lang.Keyword
125+
(specize* [k] (specize* (reg-resolve! k)))
126+
127+
clojure.lang.Symbol
128+
(specize* [s] (specize* (reg-resolve! s)))
129+
130+
clojure.spec.Spec
131+
(specize* [s] s)
132+
133+
Object
134+
(specize* [o] (spec-impl ::unknown o nil nil)))
135+
110136
(defn- specize [s]
111-
(c/or (the-spec s) (spec-impl ::unknown s nil nil)))
137+
(specize* s))
112138

113139
(defn conform
114140
"Given a spec and a value, returns :clojure.spec/invalid if value does not match spec,
@@ -279,11 +305,11 @@
279305
(defn ^:skip-wiki def-impl
280306
"Do not call this directly, use 'def'"
281307
[k form spec]
282-
(c/assert (c/and (named? k) (namespace k)) "k must be namespaced keyword or resolvable symbol")
308+
(c/assert (c/and (ident? k) (namespace k)) "k must be namespaced keyword or resolvable symbol")
283309
(let [spec (if (c/or (spec? spec) (regex? spec) (get @registry-ref spec))
284310
spec
285311
(spec-impl form spec nil nil))]
286-
(swap! registry-ref assoc k spec)
312+
(swap! registry-ref assoc k (with-name spec k))
287313
k))
288314

289315
(defn- ns-qualify
@@ -795,11 +821,13 @@
795821
(cond
796822
(spec? pred) (cond-> pred gfn (with-gen gfn))
797823
(regex? pred) (regex-spec-impl pred gfn)
798-
(named? pred) (cond-> (the-spec pred) gfn (with-gen gfn))
824+
(ident? pred) (cond-> (the-spec pred) gfn (with-gen gfn))
799825
:else
800826
(reify
801827
Spec
802-
(conform* [_ x] (dt pred x form cpred?))
828+
(conform* [_ x] (if cpred?
829+
(pred x)
830+
(if (pred x) x ::invalid)))
803831
(unform* [_ x] (if cpred?
804832
(if unc
805833
(unc x)
@@ -924,15 +952,17 @@
924952
[keys forms preds gfn]
925953
(let [id (java.util.UUID/randomUUID)
926954
kps (zipmap keys preds)
927-
cform (fn [x]
928-
(loop [i 0]
929-
(if (< i (count preds))
930-
(let [pred (preds i)]
931-
(let [ret (dt pred x (nth forms i))]
932-
(if (= ::invalid ret)
933-
(recur (inc i))
934-
(tagged-ret (keys i) ret))))
935-
::invalid)))]
955+
cform (let [specs (delay (mapv specize preds))]
956+
(fn [x]
957+
(let [specs @specs]
958+
(loop [i 0]
959+
(if (< i (count specs))
960+
(let [spec (specs i)]
961+
(let [ret (conform* spec x)]
962+
(if (= ::invalid ret)
963+
(recur (inc i))
964+
(tagged-ret (keys i) ret))))
965+
::invalid)))))]
936966
(reify
937967
Spec
938968
(conform* [_ x] (cform x))

0 commit comments

Comments
 (0)