Skip to content

Commit 71b4623

Browse files
committed
add ':missing binding-name' directive to destructuring. When set, instead of reporting an error per req!, missing keys will be (deeply) collected and bound to a map of key->nil-or-nested-missing-map. Any required bindings will be bound to nil. If nothing required is missing, yields nil
1 parent 3ae231b commit 71b4623

1 file changed

Lines changed: 39 additions & 11 deletions

File tree

src/clj/clojure/core.clj

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4513,7 +4513,7 @@
45134513
[pb bvec b v]
45144514
(let [gmap (gensym "map__")
45154515
gmapseq (with-meta gmap {:tag 'clojure.lang.ISeq})
4516-
gignore (gensym "ignore__")
4516+
gtemp (gensym "temp__")
45174517
defaults (:or b)
45184518
defaults-as (:defaults b)
45194519
_ (when (and defaults-as (not defaults))
@@ -4523,6 +4523,9 @@
45234523
select (:select b)
45244524
all (:all b)
45254525
excess (:excess b)
4526+
missing (:missing b)
4527+
gnotfound (when missing (gensym "notfound__"))
4528+
gnotfound? (when missing (gensym "notfound?__"))
45264529
xf (fn [mk]
45274530
(let [mkns (namespace mk)
45284531
mkn (name mk)]
@@ -4546,7 +4549,12 @@
45464549
(if (:as b)
45474550
(conj ret (:as b) gmap)
45484551
ret))))
4549-
bes (dissoc b :as :or :select :all :excess)
4552+
ret (if missing
4553+
(conj ret
4554+
missing nil
4555+
gnotfound (list 'new 'Object))
4556+
ret)
4557+
bes (dissoc b :as :or :select :all :excess :missing)
45504558
localize (fn [bb] (if (instance? clojure.lang.Named bb)
45514559
(with-meta (symbol nil (name bb)) (meta bb)) bb))
45524560
push1 (fn [ret bb bk req?]
@@ -4562,12 +4570,22 @@
45624570
(throw (new Exception
45634571
(str "Can't supply default value for required key: " bk)))
45644572
(list `get gmap bk (if local-default? (gdefaults local) (gdefaults bk)))))
4565-
(list getter gmap bk))]
4573+
(if req?
4574+
(if missing
4575+
(list `get gmap bk gnotfound)
4576+
(list `req! gmap bk))
4577+
(list `get gmap bk)))]
45664578
(if (ident? bb)
4567-
(-> ret (conj local bv))
4579+
(if (and req? missing)
4580+
(conj ret
4581+
gtemp bv
4582+
gnotfound? `(identical? ~gtemp ~gnotfound)
4583+
missing `(if ~gnotfound? (assoc ~missing ~bk nil) ~missing)
4584+
local `(when-not ~gnotfound? ~gtemp))
4585+
(-> ret (conj local bv)))
45684586
(pb ret bb bv))))
45694587
retsel
4570-
(loop [ret ret, sel #{}, bes bes, b->k {}, subs nil, suba nil, subd nil]
4588+
(loop [ret ret, sel #{}, bes bes, b->k {}, subs nil, suba nil, subexcess nil, submissing nil]
45714589
(if (seq bes)
45724590
(let [be (first bes), bb (key be), bk (val be)]
45734591
(if (keyword? bb)
@@ -4590,13 +4608,13 @@
45904608
"' - binding symbols can only appear before '&', use keys after"))))
45914609
bk (if preamp? (tr bb) bb)]
45924610
(recur (if (or preamp? req?)
4593-
(push1 ret (if preamp? bb gignore) bk req?)
4611+
(push1 ret (if preamp? bb gtemp) bk req?)
45944612
ret)
45954613
(conj sel bk)
45964614
(next bbs) preamp?
45974615
(if preamp? (assoc b->k (localize bb) bk) b->k)))))
45984616
{:ret ret, :sel sel, :b->k b->k}))]
4599-
(recur (:ret retsel) (:sel retsel) (next bes) (:b->k retsel) subs suba subd))
4617+
(recur (:ret retsel) (:sel retsel) (next bes) (:b->k retsel) subs suba subexcess submissing))
46004618
(let [subsel? (and select (map? bb))
46014619
bb (if (or (not subsel?) (:select bb))
46024620
bb
@@ -4613,11 +4631,17 @@
46134631
bb (if (or (not subexcess?) (:excess bb))
46144632
bb
46154633
(assoc bb :excess (gensym "excess__")))
4616-
subd (if subexcess? (assoc subd bk (:excess bb)) subd)
4634+
subexcess (if subexcess? (assoc subexcess bk (:excess bb)) subexcess)
4635+
4636+
submissing? (and missing (map? bb))
4637+
bb (if (or (not submissing?) (:missing bb))
4638+
bb
4639+
(assoc bb :missing (gensym "missing__")))
4640+
submissing (if submissing? (assoc submissing bk (:missing bb)) submissing)
46174641

46184642
b->k (if (symbol? bb) (assoc b->k bb bk) b->k)]
4619-
(recur (push1 ret bb bk false) (conj sel bk) (next bes) b->k subs suba subd))))
4620-
{:ret ret, :sel sel, :b->k b->k :subs subs :suba suba :subd subd}))
4643+
(recur (push1 ret bb bk false) (conj sel bk) (next bes) b->k subs suba subexcess submissing))))
4644+
{:ret ret, :sel sel, :b->k b->k :subs subs :suba suba :subexcess subexcess :submissing submissing}))
46214645
ret (:ret retsel), sel (:sel retsel), b->k (:b->k retsel)
46224646
new-or-code (and defaults (or defaults-as select all))
46234647
bk #(if (symbol? %)
@@ -4641,9 +4665,13 @@
46414665
ret)
46424666

46434667
ret (if excess
4644-
(conj ret excess `(merge (not-empty (apply dissoc ~gmap ~sel)) (some-vals ~(:subd retsel))))
4668+
(conj ret excess `(merge (not-empty (apply dissoc ~gmap ~sel)) (some-vals ~(:subexcess retsel))))
46454669
ret)
46464670

4671+
ret (if missing
4672+
(conj ret missing `(merge ~missing (some-vals ~(:submissing retsel))))
4673+
ret)
4674+
46474675
ret (if defaults-as (conj ret defaults-as dm) ret)]
46484676
ret))
46494677

0 commit comments

Comments
 (0)