Skip to content

Commit defa7b8

Browse files
committed
perf tweaks (and, unrolling, nonconforming in nilable)
1 parent de6a2b5 commit defa7b8

File tree

1 file changed

+88
-21
lines changed

1 file changed

+88
-21
lines changed

src/clj/clojure/spec.clj

Lines changed: 88 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -952,17 +952,39 @@
952952
[keys forms preds gfn]
953953
(let [id (java.util.UUID/randomUUID)
954954
kps (zipmap keys preds)
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)))))]
955+
specs (delay (mapv specize preds))
956+
cform (case (count preds)
957+
2 (fn [x]
958+
(let [specs @specs
959+
ret (conform* (specs 0) x)]
960+
(if (= ::invalid ret)
961+
(let [ret (conform* (specs 1) x)]
962+
(if (= ::invalid ret)
963+
::invalid
964+
(tagged-ret (keys 1) ret)))
965+
(tagged-ret (keys 0) ret))))
966+
3 (fn [x]
967+
(let [specs @specs
968+
ret (conform* (specs 0) x)]
969+
(if (= ::invalid ret)
970+
(let [ret (conform* (specs 1) x)]
971+
(if (= ::invalid ret)
972+
(let [ret (conform* (specs 2) x)]
973+
(if (= ::invalid ret)
974+
::invalid
975+
(tagged-ret (keys 2) ret)))
976+
(tagged-ret (keys 1) ret)))
977+
(tagged-ret (keys 0) ret))))
978+
(fn [x]
979+
(let [specs @specs]
980+
(loop [i 0]
981+
(if (< i (count specs))
982+
(let [spec (specs i)]
983+
(let [ret (conform* spec x)]
984+
(if (= ::invalid ret)
985+
(recur (inc i))
986+
(tagged-ret (keys i) ret))))
987+
::invalid)))))]
966988
(reify
967989
Spec
968990
(conform* [_ x] (cform x))
@@ -1014,14 +1036,42 @@
10141036
(defn ^:skip-wiki and-spec-impl
10151037
"Do not call this directly, use 'and'"
10161038
[forms preds gfn]
1017-
(reify
1018-
Spec
1019-
(conform* [_ x] (and-preds x preds forms))
1020-
(unform* [_ x] (reduce #(unform %2 %1) x (reverse preds)))
1021-
(explain* [_ path via in x] (explain-pred-list forms preds path via in x))
1022-
(gen* [_ overrides path rmap] (if gfn (gfn) (gensub (first preds) overrides path rmap (first forms))))
1023-
(with-gen* [_ gfn] (and-spec-impl forms preds gfn))
1024-
(describe* [_] `(and ~@forms))))
1039+
(let [specs (delay (mapv specize preds))
1040+
cform
1041+
(case (count preds)
1042+
2 (fn [x]
1043+
(let [specs @specs
1044+
ret (conform* (specs 0) x)]
1045+
(if (= ::invalid ret)
1046+
::invalid
1047+
(conform* (specs 1) ret))))
1048+
3 (fn [x]
1049+
(let [specs @specs
1050+
ret (conform* (specs 0) x)]
1051+
(if (= ::invalid ret)
1052+
::invalid
1053+
(let [ret (conform* (specs 1) ret)]
1054+
(if (= ::invalid ret)
1055+
::invalid
1056+
(conform* (specs 2) ret))))))
1057+
(fn [x]
1058+
(let [specs @specs]
1059+
(loop [ret x i 0]
1060+
(if (< i (count specs))
1061+
(let [nret (conform* (specs i) ret)]
1062+
(if (= ::invalid nret)
1063+
::invalid
1064+
;;propagate conformed values
1065+
(recur nret (inc i))))
1066+
ret)))))]
1067+
(reify
1068+
Spec
1069+
(conform* [_ x] (cform x))
1070+
(unform* [_ x] (reduce #(unform %2 %1) x (reverse preds)))
1071+
(explain* [_ path via in x] (explain-pred-list forms preds path via in x))
1072+
(gen* [_ overrides path rmap] (if gfn (gfn) (gensub (first preds) overrides path rmap (first forms))))
1073+
(with-gen* [_ gfn] (and-spec-impl forms preds gfn))
1074+
(describe* [_] `(and ~@forms)))))
10251075

10261076
(defn ^:skip-wiki merge-spec-impl
10271077
"Do not call this directly, use 'merge'"
@@ -1609,10 +1659,27 @@
16091659
(with-gen (clojure.spec/& (* (cat ::k keyword? ::v any?)) ::kvs->map mspec#)
16101660
(fn [] (gen/fmap (fn [m#] (apply concat m#)) (gen mspec#))))))
16111661

1662+
(defn nonconforming
1663+
"takes a spec and returns a spec that has the same properties except
1664+
'conform' returns the original (not the conformed) value. Note, will specize regex ops."
1665+
[spec]
1666+
(let [spec (specize spec)]
1667+
(reify
1668+
Spec
1669+
(conform* [_ x] (let [ret (conform* spec x)]
1670+
(if (= ::invalid ret)
1671+
::invalid
1672+
x)))
1673+
(unform* [_ x] (unform* spec x))
1674+
(explain* [_ path via in x] (explain* spec path via in x))
1675+
(gen* [_ overrides path rmap] (gen* spec overrides path rmap))
1676+
(with-gen* [_ gfn] (nonconforming (with-gen* spec gfn)))
1677+
(describe* [_] `(nonconforming ~(describe* spec))))))
1678+
16121679
(defmacro nilable
1613-
"returns a spec that accepts nil and values satisfiying pred"
1680+
"returns a spec that accepts nil and values satisfying pred"
16141681
[pred]
1615-
`(and (or ::nil nil? ::pred ~pred) (conformer second #(if (nil? %) [::nil nil] [::pred %]))))
1682+
`(nonconforming (or ::nil nil? ::pred ~pred)))
16161683

16171684
(defn exercise
16181685
"generates a number (default 10) of values compatible with spec and maps conform over them,

0 commit comments

Comments
 (0)