Skip to content

Commit

Permalink
[-let] Add support for &as bindings (magnars#115)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fuco1 committed May 3, 2015
1 parent 2cc124b commit 2db56f5
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 3 deletions.
40 changes: 40 additions & 0 deletions README.md
Expand Up @@ -1915,6 +1915,46 @@ 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).

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.

(list &as a b c) (list 1 2 3)

binds `a` to 1, `b` to 2, `c` to 3 and `list` to (1 2 3).

Similarly:

(bounds &as beg . end) (cons 1 2)

binds `beg` to 1, `end` to 2 and `bounds` to (1 . 2).

(items &as first . rest) (list 1 2 3)

binds `first` to 1, `rest` to (2 3) and `items` to (1 2 3)

[vect &as _ b c] [1 2 3]

binds `b` to 2, `c` to 3 and `vect` to [1 2 3] (_ avoids binding as usual).

(plist &as &plist :b b) (list :a 1 :b 2 :c 3)

binds `b` to 2 and `plist` to (:a 1 :b 2 :c 3). Same for &alist and &hash.

This is especially useful when we want to capture the result of a
computation and destructure at the same time. Consider the
form (function-returning-complex-structure) returning a list of
two vectors with two items each. We want to capture this entire
result and pass it to another computation, but at the same time
we want to get the second item from each vector. We can achieve
it with pattern

(result &as [_ a] [_ b]) (function-returning-complex-structure)

Note: Clojure programmers may know this feature as the ":as
binding". The difference is that we put the &as at the front
because we need to support improper list binding.

```el
(-let (([a (b c) d] [1 (2 3) 4])) (list a b c d)) ;; => '(1 2 3 4)
(-let [(a b c . d) (list 1 2 3 4 5 6)] (list a b c d)) ;; => '(1 2 3 (4 5 6))
Expand Down
59 changes: 57 additions & 2 deletions dash.el
Expand Up @@ -1452,13 +1452,28 @@ Key-value stores are disambiguated by placing a token &plist,
(dash--match-symbol match-form source))
((consp match-form)
(cond
;; Handle the "x &as" bindings first.
((and (consp (cdr match-form))
(symbolp (car match-form))
(eq '&as (cadr match-form)))
(let ((s (car match-form)))
(cons (list s source)
(dash--match (cddr match-form) s))))
((memq (car match-form) '(&plist &alist &hash))
(dash--match-kv match-form source))
((eq (car match-form) '&keys)
(dash--match-kv (cons '&plist (cdr match-form)) source))
(t (dash--match-cons match-form source))))
((vectorp match-form)
(dash--match-vector match-form source))))
;; We support the &as binding in vectors too
(cond
((and (> (length match-form) 2)
(symbolp (aref match-form 0))
(eq '&as (aref match-form 1)))
(let ((s (aref match-form 0)))
(cons (list s source)
(dash--match (dash--vector-tail match-form 2) s))))
(t (dash--match-vector match-form source))))))

(defmacro -let* (varlist &rest body)
"Bind variables according to VARLIST then eval BODY.
Expand Down Expand Up @@ -1565,7 +1580,47 @@ plist-like key-value pairs, similarly to &keys keyword of
(a1 a2 ... aN &keys key1 b1 ... keyN bK)
This binds N values from the list to a1 ... aN, then interprets
the cdr as a plist (see key/value matching above)."
the cdr as a plist (see key/value matching above).
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.
(list &as a b c) (list 1 2 3)
binds A to 1, B to 2, C to 3 and LIST to (1 2 3).
Similarly:
(bounds &as beg . end) (cons 1 2)
binds BEG to 1, END to 2 and BOUNDS to (1 . 2).
(items &as first . rest) (list 1 2 3)
binds FIRST to 1, REST to (2 3) and ITEMS to (1 2 3)
[vect &as _ b c] [1 2 3]
binds B to 2, C to 3 and VECT to [1 2 3] (_ avoids binding as usual).
(plist &as &plist :b b) (list :a 1 :b 2 :c 3)
binds B to 2 and PLIST to (:a 1 :b 2 :c 3). Same for &alist and &hash.
This is especially useful when we want to capture the result of a
computation and destructure at the same time. Consider the
form (function-returning-complex-structure) returning a list of
two vectors with two items each. We want to capture this entire
result and pass it to another computation, but at the same time
we want to get the second item from each vector. We can achieve
it with pattern
(result &as [_ a] [_ b]) (function-returning-complex-structure)
Note: Clojure programmers may know this feature as the \":as
binding\". The difference is that we put the &as at the front
because we need to support improper list binding."
(declare (debug ([&or (&rest (sexp form))
(vector [&rest [sexp form]])]
body))
Expand Down
40 changes: 40 additions & 0 deletions dash.texi
Expand Up @@ -2931,6 +2931,46 @@ 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).

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.

(list &as a b c) (list 1 2 3)

binds @var{a} to 1, @var{b} to 2, @var{c} to 3 and @var{list} to (1 2 3).

Similarly:

(bounds &as beg . end) (cons 1 2)

binds @var{beg} to 1, @var{end} to 2 and @var{bounds} to (1 . 2).

(items &as first . rest) (list 1 2 3)

binds @var{first} to 1, @var{rest} to (2 3) and @var{items} to (1 2 3)

[vect &as _ b c] [1 2 3]

binds @var{b} to 2, @var{c} to 3 and @var{vect} to [1 2 3] (_ avoids binding as usual).

(plist &as &plist :b b) (list :a 1 :b 2 :c 3)

binds @var{b} to 2 and @var{plist} to (:a 1 :b 2 :c 3). Same for &alist and &hash.

This is especially useful when we want to capture the result of a
computation and destructure at the same time. Consider the
form (function-returning-complex-structure) returning a list of
two vectors with two items each. We want to capture this entire
result and pass it to another computation, but at the same time
we want to get the second item from each vector. We can achieve
it with pattern

(result &as [_ a] [_ b]) (function-returning-complex-structure)

Note: Clojure programmers may know this feature as the ":as
binding". The difference is that we put the &as at the front
because we need to support improper list binding.

@example
@group
(-let (([a (b c) d] [1 (2 3) 4])) (list a b c d))
Expand Down
23 changes: 22 additions & 1 deletion dev/examples.el
Expand Up @@ -791,10 +791,12 @@ new list."
(-let [[a b c] "abcdef"] (list a b c)) => '(?a ?b ?c)
(-let [[a (b [c]) d] [1 (2 [3 4]) 5 6]] (list a b c d)) => '(1 2 3 5)
(-let [(a b c d) (list 1 2 3 4 5 6)] (list a b c d)) => '(1 2 3 4)
(-let [([a b]) (list (vector 1 2 3))] (list a b)) => '(1 2)
;; d is bound to nil. I don't think we want to error in such a case.
;; After all (car nil) => nil
(-let [(a b c d) (list 1 2 3)] (list a b c d)) => '(1 2 3 nil)
(-let [[a b c] [1 2 3 4]] (list a b c)) => '(1 2 3)
(-let [[a] [1 2 3 4]] a) => 1
(-let [[a b &rest c] "abcdef"] (list a b c)) => '(?a ?b "cdef")
(-let [[a b &rest c] [1 2 3 4 5 6]] (list a b c)) => '(1 2 [3 4 5 6])
(-let [[a b &rest [c d]] [1 2 3 4 5 6]] (list a b c d)) => '(1 2 3 4)
Expand Down Expand Up @@ -872,7 +874,26 @@ new list."
(-let [[(a _ b)] (vector (list 1 2 3 4))] (list a b)) => '(1 3)
(-let [(&plist 'a a) (list 'a 1 'b 2)] a) => 1
(-let [(&plist 'a [a b]) (list 'a [1 2] 'b 3)] (list a b)) => '(1 2)
(-let [(&plist 'a [a b] 'c c) (list 'a [1 2] 'c 3)] (list a b c)) => '(1 2 3))
(-let [(&plist 'a [a b] 'c c) (list 'a [1 2] 'c 3)] (list a b c)) => '(1 2 3)
;; 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))
(-let [(all &as (list &as a b) bar) (list (list 1 2) 3)] (list a b bar list all)) => '(1 2 3 (1 2) ((1 2) 3))
(-let [(x &as [a b]) (list (vector 1 2 3))] (list a b x)) => '(1 2 ([1 2 3]))
(-let [(result &as [_ a] [_ b]) (list [1 2] [3 4])] (list a b result)) => '(2 4 ([1 2] [3 4]))
(-let [(result &as [fst &as _ a] [snd &as _ b]) (list [1 2] [3 4])] (list a b fst snd result)) => '(2 4 [1 2] [3 4] ([1 2] [3 4]))
(-let [[x &as a b &rest r] (vector 1 2 3)] (list a b r x)) => '(1 2 [3] [1 2 3])
(-let [[x &as a] (vector 1 2 3)] (list a x)) => '(1 [1 2 3])
(-let [[x &as _ _ a] (vector 1 2 3)] (list a x)) => '(3 [1 2 3])
(-let [[x &as _ _ a] (vector 1 2 (list 3 4))] (list a x)) => '((3 4) [1 2 (3 4)])
(-let [[x &as _ _ (a b)] (vector 1 2 (list 3 4))] (list a b x)) => '(3 4 [1 2 (3 4)])
(-let [(b &as beg . end) (cons 1 2)] (list beg end b)) => '(1 2 (1 . 2))
(-let [(plist &as &plist :a a :b b) (list :a 1 :b 2)] (list a b plist)) => '(1 2 (:a 1 :b 2))
(-let [(alist &as &alist :a a :b b) (list (cons :a 1) (cons :b 2))] (list a b alist)) => '(1 2 ((:a . 1) (:b . 2)))
(-let [(list &as _ _ _ a _ _ _ b _ _ _ c) (list 1 2 3 4 5 6 7 8 9 10 11 12)] (list a b c list)) => '(4 8 12 (1 2 3 4 5 6 7 8 9 10 11 12))
(-let (((x &as a b) (list 1 2))
((y &as c d) (list 3 4)))
(list a b c d x y)) => '(1 2 3 4 (1 2) (3 4)))

(defexamples -let*
(-let* (((a . b) (cons 1 2))
Expand Down

0 comments on commit 2db56f5

Please sign in to comment.