[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/dash e52909f 110/316: Add smarter key destructuring
From: |
ELPA Syncer |
Subject: |
[elpa] externals/dash e52909f 110/316: Add smarter key destructuring |
Date: |
Mon, 15 Feb 2021 15:57:37 -0500 (EST) |
branch: externals/dash
commit e52909f0989bd562ce3e6cc84bc7c64ccc2f6fd8
Author: Matus Goljer <matus.goljer@gmail.com>
Commit: Matus Goljer <matus.goljer@gmail.com>
Add smarter key destructuring
---
README.md | 33 +++++++++++++++++
dash.el | 78 ++++++++++++++++++++++++++++++++++++++--
dash.info | 109 ++++++++++++++++++++++++++++++++++++--------------------
dash.texi | 33 +++++++++++++++++
dev/examples.el | 29 +++++++++++++++
5 files changed, 242 insertions(+), 40 deletions(-)
diff --git a/README.md b/README.md
index 7fa86c1..1070672 100644
--- a/README.md
+++ b/README.md
@@ -2324,14 +2324,17 @@ Key/value stores:
(&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the
`source` plist to aK. If the
value is not found, aK is nil.
+ Uses `plist-get` to fetch values.
(&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the
`source` alist to aK. If the
value is not found, aK is nil.
+ Uses `assoc` to fetch values.
(&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the
`source` hash table to aK. If the
value is not found, aK is nil.
+ Uses `gethash` to fetch values.
Further, special keyword &keys supports "inline" matching of
plist-like key-value pairs, similarly to &keys keyword of
@@ -2342,6 +2345,36 @@ plist-like key-value pairs, similarly to &keys keyword of
This binds `n` values from the list to a1 ... aN, then interprets
the cdr as a plist (see key/value matching above).
+`a` shorthand notation for kv-destructuring exists which allows the
+patterns be optionally left out and derived from the key name in
+the following fashion:
+
+- a key :foo is converted into `foo` pattern,
+- a key 'bar is converted into `bar` pattern,
+- a key "baz" is converted into `baz` pattern.
+
+That is, the entire value under the key is bound to the derived
+variable without any further destructuring.
+
+This is possible only when the form following the key is not a
+valid pattern (i.e. not a symbol, a cons cell or a vector).
+Otherwise the matching proceeds as usual and in case of an
+invalid spec fails with an error.
+
+Thus the patterns are normalized as follows:
+
+ ;; derive all the missing patterns
+ (&plist :foo 'bar "baz") => (&plist :foo foo 'bar bar "baz" baz)
+
+ ;; we can specify some but not others
+ (&plist :foo 'bar explicit-bar) => (&plist :foo foo 'bar explicit-bar)
+
+ ;; nothing happens, we store :foo in x
+ (&plist :foo x) => (&plist :foo x)
+
+ ;; nothing happens, we match recursively
+ (&plist :foo (a b c)) => (&plist :foo (a b c))
+
You can name the source using the syntax `symbol` &as `pattern`.
This syntax works with lists (proper or improper), vectors and
all types of maps.
diff --git a/dash.el b/dash.el
index a115a43..a9357ca 100644
--- a/dash.el
+++ b/dash.el
@@ -1618,7 +1618,7 @@ SOURCE is a proper or improper list."
(cond
((and (symbolp (car match-form))
(memq (car match-form) '(&keys &plist &alist &hash)))
- (dash--match-kv match-form (dash--match-cons-get-cdr skip-cdr
source)))
+ (dash--match-kv (dash--match-kv-normalize-match-form match-form)
(dash--match-cons-get-cdr skip-cdr source)))
((dash--match-ignore-place-p (car match-form))
(dash--match-cons-1 (cdr match-form) source
(plist-put props :skip-cdr (1+ skip-cdr))))
@@ -1702,6 +1702,47 @@ is discarded."
(setq i (1+ i))))
(-flatten-n 1 (nreverse re))))
+(defun dash--match-kv-normalize-match-form (pattern)
+ "Normalize kv PATTERN.
+
+This method normalizes PATTERN to the format expected by
+`dash--match-kv'. See `-let' for the specification."
+ (let ((normalized (list (car pattern)))
+ (skip nil)
+ (fill-placeholder (make-symbol "--dash-fill-placeholder--")))
+ (-each (apply '-zip (-pad fill-placeholder (cdr pattern) (cddr pattern)))
+ (lambda (pair)
+ (let ((current (car pair))
+ (next (cdr pair)))
+ (if skip
+ (setq skip nil)
+ (if (or (eq fill-placeholder next)
+ (not (or (and (symbolp next)
+ (not (keywordp next))
+ (not (eq next t))
+ (not (eq next nil)))
+ (and (consp next)
+ (not (eq (car next) 'quote)))
+ (vectorp next))))
+ (progn
+ (cond
+ ((keywordp current)
+ (push current normalized)
+ (push (intern (substring (symbol-name current) 1))
normalized))
+ ((stringp current)
+ (push current normalized)
+ (push (intern current) normalized))
+ ((and (consp current)
+ (eq (car current) 'quote))
+ (push current normalized)
+ (push (cadr current) normalized))
+ (t (error "-let: found key `%s' in kv destructuring but its
pattern `%s' is invalid and can not be derived from the key" current next)))
+ (setq skip nil))
+ (push current normalized)
+ (push next normalized)
+ (setq skip t))))))
+ (nreverse normalized)))
+
(defun dash--match-kv (match-form source)
"Setup a kv matching environment and call the real matcher.
@@ -1774,7 +1815,7 @@ Key-value stores are disambiguated by placing a token
&plist,
(cons (list s source)
(dash--match (cddr match-form) s))))
((memq (car match-form) '(&keys &plist &alist &hash))
- (dash--match-kv match-form source))
+ (dash--match-kv (dash--match-kv-normalize-match-form match-form) source))
(t (dash--match-cons match-form source))))
((vectorp match-form)
;; We support the &as binding in vectors too
@@ -1876,14 +1917,17 @@ Key/value stores:
(&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the
SOURCE plist to aK. If the
value is not found, aK is nil.
+ Uses `plist-get' to fetch values.
(&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the
SOURCE alist to aK. If the
value is not found, aK is nil.
+ Uses `assoc' to fetch values.
(&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the
SOURCE hash table to aK. If the
value is not found, aK is nil.
+ Uses `gethash' to fetch values.
Further, special keyword &keys supports \"inline\" matching of
plist-like key-value pairs, similarly to &keys keyword of
@@ -1894,6 +1938,36 @@ plist-like key-value pairs, similarly to &keys keyword of
This binds N values from the list to a1 ... aN, then interprets
the cdr as a plist (see key/value matching above).
+A shorthand notation for kv-destructuring exists which allows the
+patterns be optionally left out and derived from the key name in
+the following fashion:
+
+- a key :foo is converted into `foo' pattern,
+- a key 'bar is converted into `bar' pattern,
+- a key \"baz\" is converted into `baz' pattern.
+
+That is, the entire value under the key is bound to the derived
+variable without any further destructuring.
+
+This is possible only when the form following the key is not a
+valid pattern (i.e. not a symbol, a cons cell or a vector).
+Otherwise the matching proceeds as usual and in case of an
+invalid spec fails with an error.
+
+Thus the patterns are normalized as follows:
+
+ ;; derive all the missing patterns
+ (&plist :foo 'bar \"baz\") => (&plist :foo foo 'bar bar \"baz\" baz)
+
+ ;; we can specify some but not others
+ (&plist :foo 'bar explicit-bar) => (&plist :foo foo 'bar explicit-bar)
+
+ ;; nothing happens, we store :foo in x
+ (&plist :foo x) => (&plist :foo x)
+
+ ;; nothing happens, we match recursively
+ (&plist :foo (a b c)) => (&plist :foo (a b c))
+
You can name the source using the syntax SYMBOL &as PATTERN.
This syntax works with lists (proper or improper), vectors and
all types of maps.
diff --git a/dash.info b/dash.info
index 5d10cc8..e92ab33 100644
--- a/dash.info
+++ b/dash.info
@@ -2245,13 +2245,16 @@ control.
Key/value stores:
(&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the
- SOURCE plist to aK. If the value is not found, aK is nil.
+ SOURCE plist to aK. If the value is not found, aK is nil. Uses
+ ‘plist-get’ to fetch values.
(&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the
- SOURCE alist to aK. If the value is not found, aK is nil.
+ SOURCE alist to aK. If the value is not found, aK is nil. Uses
+ ‘assoc’ to fetch values.
(&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the
SOURCE hash table to aK. If the value is not found, aK is nil.
+ Uses ‘gethash’ to fetch values.
Further, special keyword &keys supports "inline" matching of
plist-like key-value pairs, similarly to &keys keyword of
@@ -2262,6 +2265,36 @@ control.
This binds N values from the list to a1 ... aN, then interprets
the cdr as a plist (see key/value matching above).
+ A shorthand notation for kv-destructuring exists which allows the
+ patterns be optionally left out and derived from the key name in
+ the following fashion:
+
+ - a key :foo is converted into ‘foo’ pattern, - a key ’bar is
+ converted into ‘bar’ pattern, - a key "baz" is converted into
+ ‘baz’ pattern.
+
+ That is, the entire value under the key is bound to the derived
+ variable without any further destructuring.
+
+ This is possible only when the form following the key is not a
+ valid pattern (i.e. not a symbol, a cons cell or a vector).
+ Otherwise the matching proceeds as usual and in case of an
+ invalid spec fails with an error.
+
+ Thus the patterns are normalized as follows:
+
+ ;; derive all the missing patterns (&plist :foo ’bar "baz") =>
+ (&plist :foo foo ’bar bar "baz" baz)
+
+ ;; we can specify some but not others (&plist :foo ’bar
+ explicit-bar) => (&plist :foo foo ’bar explicit-bar)
+
+ ;; nothing happens, we store :foo in x (&plist :foo x) => (&plist
+ :foo x)
+
+ ;; nothing happens, we match recursively (&plist :foo (a b c)) =>
+ (&plist :foo (a b c))
+
You can name the source using the syntax SYMBOL &as PATTERN.
This syntax works with lists (proper or improper), vectors and
all types of maps.
@@ -2976,13 +3009,13 @@ Index
* -juxt: Function combinators.
(line 31)
* -keep: List to list. (line 8)
-* -lambda: Binding. (line 220)
+* -lambda: Binding. (line 253)
* -last: Other list operations.
(line 232)
* -last-item: Other list operations.
(line 301)
* -let: Binding. (line 66)
-* -let*: Binding. (line 200)
+* -let*: Binding. (line 233)
* -list: Other list operations.
(line 334)
* -map: Maps. (line 10)
@@ -3056,7 +3089,7 @@ Index
* -select-column: Sublist selection. (line 199)
* -select-columns: Sublist selection. (line 180)
* -separate: Partitioning. (line 63)
-* -setq: Binding. (line 243)
+* -setq: Binding. (line 276)
* -slice: Sublist selection. (line 86)
* -snoc: Other list operations.
(line 42)
@@ -3271,39 +3304,39 @@ Ref: -when-let*71822
Ref: -if-let72350
Ref: -if-let*72745
Ref: -let73362
-Ref: -let*78155
-Ref: -lambda79096
-Ref: -setq79898
-Node: Side-effects80714
-Ref: -each80908
-Ref: -each-while81315
-Ref: -each-indexed81675
-Ref: -dotimes82193
-Ref: -doto82496
-Node: Destructive operations82923
-Ref: !cons83096
-Ref: !cdr83302
-Node: Function combinators83497
-Ref: -partial83771
-Ref: -rpartial84166
-Ref: -juxt84568
-Ref: -compose85000
-Ref: -applify85558
-Ref: -on86005
-Ref: -flip86528
-Ref: -const86840
-Ref: -cut87184
-Ref: -not87670
-Ref: -orfn87980
-Ref: -andfn88414
-Ref: -iteratefn88909
-Ref: -fixfn89612
-Ref: -prodfn91181
-Node: Development92247
-Node: Contribute92596
-Node: Changes93344
-Node: Contributors96343
-Node: Index97967
+Ref: -let*79450
+Ref: -lambda80391
+Ref: -setq81193
+Node: Side-effects82009
+Ref: -each82203
+Ref: -each-while82610
+Ref: -each-indexed82970
+Ref: -dotimes83488
+Ref: -doto83791
+Node: Destructive operations84218
+Ref: !cons84391
+Ref: !cdr84597
+Node: Function combinators84792
+Ref: -partial85066
+Ref: -rpartial85461
+Ref: -juxt85863
+Ref: -compose86295
+Ref: -applify86853
+Ref: -on87300
+Ref: -flip87823
+Ref: -const88135
+Ref: -cut88479
+Ref: -not88965
+Ref: -orfn89275
+Ref: -andfn89709
+Ref: -iteratefn90204
+Ref: -fixfn90907
+Ref: -prodfn92476
+Node: Development93542
+Node: Contribute93891
+Node: Changes94639
+Node: Contributors97638
+Node: Index99262
End Tag Table
diff --git a/dash.texi b/dash.texi
index 4fb616d..2243a34 100644
--- a/dash.texi
+++ b/dash.texi
@@ -3564,14 +3564,17 @@ Key/value stores:
(&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the
@var{source} plist to aK. If the
value is not found, aK is nil.
+ Uses @code{plist-get} to fetch values.
(&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the
@var{source} alist to aK. If the
value is not found, aK is nil.
+ Uses @code{assoc} to fetch values.
(&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the
@var{source} hash table to aK. If the
value is not found, aK is nil.
+ Uses @code{gethash} to fetch values.
Further, special keyword &keys supports "inline" matching of
plist-like key-value pairs, similarly to &keys keyword of
@@ -3582,6 +3585,36 @@ plist-like key-value pairs, similarly to &keys keyword of
This binds @var{n} values from the list to a1 ... aN, then interprets
the cdr as a plist (see key/value matching above).
+@var{a} shorthand notation for kv-destructuring exists which allows the
+patterns be optionally left out and derived from the key name in
+the following fashion:
+
+- a key :foo is converted into @code{foo} pattern,
+- a key 'bar is converted into @code{bar} pattern,
+- a key "baz" is converted into @code{baz} pattern.
+
+That is, the entire value under the key is bound to the derived
+variable without any further destructuring.
+
+This is possible only when the form following the key is not a
+valid pattern (i.e. not a symbol, a cons cell or a vector).
+Otherwise the matching proceeds as usual and in case of an
+invalid spec fails with an error.
+
+Thus the patterns are normalized as follows:
+
+ ;; derive all the missing patterns
+ (&plist :foo 'bar "baz") => (&plist :foo foo 'bar bar "baz" baz)
+
+ ;; we can specify some but not others
+ (&plist :foo 'bar explicit-bar) => (&plist :foo foo 'bar explicit-bar)
+
+ ;; nothing happens, we store :foo in x
+ (&plist :foo x) => (&plist :foo x)
+
+ ;; nothing happens, we match recursively
+ (&plist :foo (a b c)) => (&plist :foo (a b c))
+
You can name the source using the syntax @var{symbol} &as @var{pattern}.
This syntax works with lists (proper or improper), vectors and
all types of maps.
diff --git a/dev/examples.el b/dev/examples.el
index bd35559..5fda281 100644
--- a/dev/examples.el
+++ b/dev/examples.el
@@ -1100,6 +1100,35 @@ new list."
(-let (((_ _ . (&alist 'a a 'c c)) (list 1 2 '(a . b) '(e . f) '(g . h)
'(c . d)))) (list a c)) => '(b d)
(-let (((x y (&alist 'a a 'c c)) (list 1 2 '((a . b) (e . f) (g . h) (c .
d))))) (list x y a c)) => '(1 2 b d)
(-let (((_ _ . ((&alist 'a a 'c c))) (list 1 2 '((a . b) (e . f) (g . h)
(c . d))))) (list a c)) => '(b d)
+ ;; auto-derived match forms for kv destructuring
+ ;;; test that we normalize all the supported kv stores
+ (-let (((&plist :foo :bar) (list :foo 1 :bar 2))) (list foo bar)) => '(1 2)
+ (-let (((&alist :foo :bar) (list (cons :foo 1) (cons :bar 2)))) (list foo
bar)) => '(1 2)
+ (let ((hash (make-hash-table)))
+ (puthash :foo 1 hash)
+ (puthash :bar 2 hash)
+ (-let (((&hash :foo :bar) hash)) (list foo bar))) => '(1 2)
+ (-let (((_ &keys :foo :bar) (list 'ignored :foo 1 :bar 2))) (list foo
bar)) => '(1 2)
+ ;;; go over all the variations of match-form derivation
+ (-let (((&plist :foo foo :bar) (list :foo 1 :bar 2))) (list foo bar)) =>
'(1 2)
+ (-let (((&plist :foo foo :bar bar) (list :foo 1 :bar 2))) (list foo bar))
=> '(1 2)
+ (-let (((&plist :foo x :bar y) (list :foo 1 :bar 2))) (list x y)) => '(1 2)
+ (-let (((&plist :foo (x) :bar [y]) (list :foo (list 1) :bar (vector 2))))
(list x y)) => '(1 2)
+ (-let (((&plist 'foo 'bar) (list 'foo 1 'bar 2))) (list foo bar)) => '(1 2)
+ (-let (((&plist 'foo foo 'bar) (list 'foo 1 'bar 2))) (list foo bar)) =>
'(1 2)
+ (-let (((&plist 'foo foo 'bar bar) (list 'foo 1 'bar 2))) (list foo bar))
=> '(1 2)
+ (-let (((&plist 'foo x 'bar y) (list 'foo 1 'bar 2))) (list x y)) => '(1 2)
+ (-let (((&alist "foo" "bar") (list (cons "foo" 1) (cons "bar" 2)))) (list
foo bar)) => '(1 2)
+ (-let (((&alist "foo" x "bar") (list (cons "foo" 1) (cons "bar" 2))))
(list x bar)) => '(1 2)
+ (-let (((&alist "foo" x "bar" y) (list (cons "foo" 1) (cons "bar" 2))))
(list x y)) => '(1 2)
+ (-let (((&alist :a 'b "c") (list (cons :a 1) (cons 'b 2) (cons "c" 3))))
(list a b c)) => '(1 2 3)
+ (-let (((&alist 'b :a "c") (list (cons :a 1) (cons 'b 2) (cons "c" 3))))
(list a b c)) => '(1 2 3)
+ (-let (((&alist 'b "c" :a) (list (cons :a 1) (cons 'b 2) (cons "c" 3))))
(list a b c)) => '(1 2 3)
+ (-let (((&alist "c" 'b :a) (list (cons :a 1) (cons 'b 2) (cons "c" 3))))
(list a b c)) => '(1 2 3)
+ (-let (((&alist "c" :a 'b) (list (cons :a 1) (cons 'b 2) (cons "c" 3))))
(list a b c)) => '(1 2 3)
+ (-let (((&alist :a "c" 'b) (list (cons :a 1) (cons 'b 2) (cons "c" 3))))
(list a b c)) => '(1 2 3)
+ (-let (((&plist 'foo 1) (list 'foo 'bar))) (list foo)) !!> error
+ (-let (((&plist foo :bar) (list :foo :bar))) (list foo)) !!> error
;; test the &as form
(-let (((items &as first . rest) (list 1 2 3))) (list first rest items))
=> '(1 (2 3) (1 2 3))
(-let [(all &as [vect &as a b] bar) (list [1 2] 3)] (list a b bar vect
all)) => '(1 2 3 [1 2] ([1 2] 3))
- [elpa] externals/dash 0574686 089/316: Make tails and inits return all prefixes including the empty one., (continued)
- [elpa] externals/dash 0574686 089/316: Make tails and inits return all prefixes including the empty one., ELPA Syncer, 2021/02/15
- [elpa] externals/dash eb1231e 087/316: Add -reductions[-r][-from], ELPA Syncer, 2021/02/15
- [elpa] externals/dash 4e90743 093/316: Ensure ' in docstring code snippets is not replaced, ELPA Syncer, 2021/02/15
- [elpa] externals/dash 138c22a 097/316: Merge pull request #261 from basil-conto/blc/common-prefix, ELPA Syncer, 2021/02/15
- [elpa] externals/dash 3789b15 102/316: Change Emacs tested and supported versions., ELPA Syncer, 2021/02/15
- [elpa] externals/dash e9919f6 104/316: Release 2.14.0, ELPA Syncer, 2021/02/15
- [elpa] externals/dash a55452a 107/316: Merge pull request #270 from magnars/travis/use-emacs-26, ELPA Syncer, 2021/02/15
- [elpa] externals/dash 14f76df 118/316: Update docs, ELPA Syncer, 2021/02/15
- [elpa] externals/dash 8af9987 116/316: Add '-each-r' and '-each-r-while'., ELPA Syncer, 2021/02/15
- [elpa] externals/dash 8873749 120/316: Improve and simplify right-associative reductions, ELPA Syncer, 2021/02/15
- [elpa] externals/dash e52909f 110/316: Add smarter key destructuring,
ELPA Syncer <=
- [elpa] externals/dash 6302243 127/316: Release 2.15.0, ELPA Syncer, 2021/02/15
- [elpa] externals/dash 2758ee7 130/316: Alias `-cons-pair-p` to `-cons-pair?`, ELPA Syncer, 2021/02/15
- [elpa] externals/dash 7267556 075/316: Fix infinite loop in -zip/-interleave when called with no arguments., ELPA Syncer, 2021/02/15
- [elpa] externals/dash fd5980e 136/316: Ensure `hash?` expander evaluates its arg only once., ELPA Syncer, 2021/02/15
- [elpa] externals/dash dca7bdc 135/316: Merge pull request #277 from yyoncho/custom-destructoring, ELPA Syncer, 2021/02/15
- [elpa] externals/dash 7854ec7 148/316: Fix quote simplification in docs, ELPA Syncer, 2021/02/15
- [elpa] externals/dash 4ce0e85 149/316: Actually remove mentions of Marmalade, ELPA Syncer, 2021/02/15
- [elpa] externals/dash 0273913 072/316: Update docs, ELPA Syncer, 2021/02/15
- [elpa] externals/dash e9b8611 077/316: Mention that -select and -remove are related, ELPA Syncer, 2021/02/15
- [elpa] externals/dash 8ef3287 078/316: Merge pull request #237 from Wilfred/add-see-also, ELPA Syncer, 2021/02/15