branch: externals/dash commit 8c4e27fa7e8d31cb60d62bc27a22e82408af0c8f Author: Matus Goljer <matus.gol...@gmail.com> Commit: Matus Goljer <matus.gol...@gmail.com>
Implement -setq --- README.md | 30 +++++++++++++++++++ dash.el | 54 ++++++++++++++++++++++++++++++++++ dash.info | 91 +++++++++++++++++++++++++++++++++++++-------------------- dash.texi | 39 +++++++++++++++++++++++++ dev/examples.el | 12 +++++++- 5 files changed, 194 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 3c9d006..7fa86c1 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,7 @@ Convenient versions of `let` and `let*` constructs combined with flow control. * [-let](#-let-varlist-rest-body) `(varlist &rest body)` * [-let*](#-let-varlist-rest-body) `(varlist &rest body)` * [-lambda](#-lambda-match-form-rest-body) `(match-form &rest body)` +* [-setq](#-setq-rest-forms) `(&rest forms)` ### Side-effects @@ -2429,6 +2430,35 @@ See [`-let`](#-let-varlist-rest-body) for the description of destructuring mecha (funcall (-lambda ((_ . a) (_ . b)) (-concat a b)) '(1 2 3) '(4 5 6)) ;; => '(2 3 5 6) ``` +#### -setq `(&rest forms)` + +Bind each `match-form` to the value of its `val`. + +`match-form` destructuring is done according to the rules of [`-let`](#-let-varlist-rest-body). + +This macro allows you to bind multiple variables by destructuring +the value, so for example: + + (-setq (a b) x + (&plist :c c) plist) + +expands roughly speaking to the following code + + (setq a (car x) + b (cadr x) + c (plist-get plist :c)) + +Care is taken to only evaluate each `val` once so that in case of +multiple assignments it does not cause unexpected side effects. + +(fn [`match-form` `val`]...) + +```el +(progn (-setq a 1) a) ;; => 1 +(progn (-setq (a b) (list 1 2)) (list a b)) ;; => '(1 2) +(progn (-setq (&plist :c c) (list :c "c")) c) ;; => "c" +``` + ## Side-effects diff --git a/dash.el b/dash.el index 89c01b5..a115a43 100644 --- a/dash.el +++ b/dash.el @@ -1978,6 +1978,60 @@ See `-let' for the description of destructuring mechanism." `(lambda ,(--map (cadr it) inputs) (-let* ,inputs ,@body)))))) +(defmacro -setq (&rest forms) + "Bind each MATCH-FORM to the value of its VAL. + +MATCH-FORM destructuring is done according to the rules of `-let'. + +This macro allows you to bind multiple variables by destructuring +the value, so for example: + + (-setq (a b) x + (&plist :c c) plist) + +expands roughly speaking to the following code + + (setq a (car x) + b (cadr x) + c (plist-get plist :c)) + +Care is taken to only evaluate each VAL once so that in case of +multiple assignments it does not cause unexpected side effects. + +(fn [MATCH-FORM VAL]...)" + (declare (debug (&rest sexp form)) + (indent 1)) + (when (= (mod (length forms) 2) 1) + (error "Odd number of arguments")) + (let* ((forms-and-sources + ;; First get all the necessary mappings with all the + ;; intermediate bindings. + (-map (lambda (x) (dash--match (car x) (cadr x))) + (-partition 2 forms))) + ;; To preserve the logic of dynamic scoping we must ensure + ;; that we `setq' the variables outside of the `let*' form + ;; which holds the destructured intermediate values. For + ;; this we generate for each variable a placeholder which is + ;; bound to (lexically) the result of the destructuring. + ;; Then outside of the helper `let*' form we bind all the + ;; original variables to their respective placeholders. + ;; TODO: There is a lot of room for possible optimization, + ;; for start playing with `special-variable-p' to eliminate + ;; unnecessary re-binding. + (variables-to-placeholders + (-mapcat + (lambda (bindings) + (-map + (lambda (binding) + (let ((var (car binding))) + (list var (make-symbol (concat "--dash-binding-" (symbol-name var) "--"))))) + (--filter (not (string-prefix-p "--" (symbol-name (car it)))) bindings))) + forms-and-sources))) + `(let ,(-map 'cadr variables-to-placeholders) + (let* ,(-flatten-n 1 forms-and-sources) + (setq ,@(-flatten (-map 'reverse variables-to-placeholders)))) + (setq ,@(-flatten variables-to-placeholders))))) + (defmacro -if-let* (vars-vals then &rest else) "If all VALS evaluate to true, bind them to their corresponding VARS and do THEN, otherwise do ELSE. VARS-VALS should be a list diff --git a/dash.info b/dash.info index 3116c4b..5d10cc8 100644 --- a/dash.info +++ b/dash.info @@ -1,4 +1,4 @@ -This is dash.info, produced by makeinfo version 6.5 from dash.texi. +This is dash.info, produced by makeinfo version 6.1 from dash.texi. This manual is for ‘dash.el’ version 2.12.1. @@ -2354,6 +2354,33 @@ control. (funcall (-lambda ((_ . a) (_ . b)) (-concat a b)) '(1 2 3) '(4 5 6)) ⇒ '(2 3 5 6) + -- Macro: -setq (&rest forms) + Bind each MATCH-FORM to the value of its VAL. + + MATCH-FORM destructuring is done according to the rules of ‘-let’ + (*note -let::). + + This macro allows you to bind multiple variables by destructuring + the value, so for example: + + (-setq (a b) x (&plist :c c) plist) + + expands roughly speaking to the following code + + (setq a (car x) b (cadr x) c (plist-get plist :c)) + + Care is taken to only evaluate each VAL once so that in case of + multiple assignments it does not cause unexpected side effects. + + (fn [MATCH-FORM VAL]...) + + (progn (-setq a 1) a) + ⇒ 1 + (progn (-setq (a b) (list 1 2)) (list a b)) + ⇒ '(1 2) + (progn (-setq (&plist :c c) (list :c "c")) c) + ⇒ "c" + File: dash.info, Node: Side-effects, Next: Destructive operations, Prev: Binding, Up: Functions @@ -3029,6 +3056,7 @@ Index * -select-column: Sublist selection. (line 199) * -select-columns: Sublist selection. (line 180) * -separate: Partitioning. (line 63) +* -setq: Binding. (line 243) * -slice: Sublist selection. (line 86) * -snoc: Other list operations. (line 42) @@ -3245,36 +3273,37 @@ Ref: -if-let*72745 Ref: -let73362 Ref: -let*78155 Ref: -lambda79096 -Node: Side-effects79898 -Ref: -each80092 -Ref: -each-while80499 -Ref: -each-indexed80859 -Ref: -dotimes81377 -Ref: -doto81680 -Node: Destructive operations82107 -Ref: !cons82280 -Ref: !cdr82486 -Node: Function combinators82681 -Ref: -partial82955 -Ref: -rpartial83350 -Ref: -juxt83752 -Ref: -compose84184 -Ref: -applify84742 -Ref: -on85189 -Ref: -flip85712 -Ref: -const86024 -Ref: -cut86368 -Ref: -not86854 -Ref: -orfn87164 -Ref: -andfn87598 -Ref: -iteratefn88093 -Ref: -fixfn88796 -Ref: -prodfn90365 -Node: Development91431 -Node: Contribute91780 -Node: Changes92528 -Node: Contributors95527 -Node: Index97151 +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 End Tag Table diff --git a/dash.texi b/dash.texi index e6a1ffb..4fb616d 100644 --- a/dash.texi +++ b/dash.texi @@ -3700,6 +3700,45 @@ See @code{-let} (@pxref{-let}) for the description of destructuring mechanism. @end example @end defmac +@anchor{-setq} +@defmac -setq (&rest forms) +Bind each @var{match-form} to the value of its @var{val}. + +@var{match-form} destructuring is done according to the rules of @code{-let} (@pxref{-let}). + +This macro allows you to bind multiple variables by destructuring +the value, so for example: + + (-setq (a b) x + (&plist :c c) plist) + +expands roughly speaking to the following code + + (setq a (car x) + b (cadr x) + c (plist-get plist :c)) + +Care is taken to only evaluate each @var{val} once so that in case of +multiple assignments it does not cause unexpected side effects. + +(fn [@var{match-form} @var{val}]...) + +@example +@group +(progn (-setq a 1) a) + @result{} 1 +@end group +@group +(progn (-setq (a b) (list 1 2)) (list a b)) + @result{} '(1 2) +@end group +@group +(progn (-setq (&plist :c c) (list :c "c")) c) + @result{} "c" +@end group +@end example +@end defmac + @node Side-effects @section Side-effects diff --git a/dev/examples.el b/dev/examples.el index 37c1fd9..bd35559 100644 --- a/dev/examples.el +++ b/dev/examples.el @@ -1146,7 +1146,17 @@ new list." (funcall (-lambda ((a) (b)) (+ a b)) '(1 2 3) '(4 5 6)) => 5 (-lambda a t) !!> wrong-type-argument (funcall (-lambda (a b) (+ a b)) 1 2) => 3 - (funcall (-lambda (a (b c)) (+ a b c)) 1 (list 2 3)) => 6)) + (funcall (-lambda (a (b c)) (+ a b c)) 1 (list 2 3)) => 6) + + (defexamples -setq + (progn (-setq a 1) a) => 1 + (progn (-setq (a b) (list 1 2)) (list a b)) => '(1 2) + (progn (-setq (&plist :c c) (list :c "c")) c) => "c" + (progn (-setq a 1 b 2) (list a b)) => '(1 2) + (progn (-setq (&plist :a a) (list :a (list :b 1)) + (&plist :b b) a) b) => 1 + (-setq (a b (&plist 'x x 'y y)) (list 1 2 (list 'x 3 'y 4)) + z x) => 3)) (def-example-group "Side-effects" "Functions iterating over lists for side-effect only."