emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/dash 5281702e36 2/8: Close low-flying zips


From: ELPA Syncer
Subject: [elpa] externals/dash 5281702e36 2/8: Close low-flying zips
Date: Sat, 4 Mar 2023 17:57:35 -0500 (EST)

branch: externals/dash
commit 5281702e36e87164d2489abc95441b88ca1d4b10
Author: Basil L. Contovounesios <contovob@tcd.ie>
Commit: Basil L. Contovounesios <contovob@tcd.ie>

    Close low-flying zips
    
    * NEWS.md (2.20.0): Announce deprecation of dyadic -zip calling
    convention.
    
    * readme-template.md (Change log): Retract promise to
    break -zip (issue #400).
    
    * dash.el (--reject-first): Pacify Emacs 24 macro #'-quoting
    warning.
    (-remove-at, -zip-fill): Optimize.  Extend docstring.
    (-remove-at-indices): Handle empty list input.  Optimize.  Clarify
    docstring.
    (--zip-with, -zip-lists): Simplify.  Extend docstring.
    (-zip-with): Fix docstring.
    (-zip-lists-fill, -unzip-lists, dash--length=)
    (dash--zip-lists-or-pair): New functions.
    (-zip): Replace anonymous compiler-macro with
    dash--zip-lists-or-pair for the benefit of Emacs 24.3.  Use
    dash--length= in place of length.  Clarify docstring.
    (-zip-pair): Make variadic again for backward compatibility with
    when it used to be an alias of -zip, and declare a dyadic
    advertised-calling-convention instead.  Delegate to -zip-lists as
    needed.  Clarify docstring.
    (-unzip): Clarify docstring (issue #400).
    (dash--match-kv-normalize-match-form, -let): Simplify.
    (-prodfn): Optimize.
    
    Fixes #400.
---
 NEWS.md            |  11 +-
 dash.el            | 288 +++++++++++++++++++++++++++++++++++------------------
 readme-template.md |  13 ---
 3 files changed, 200 insertions(+), 112 deletions(-)

diff --git a/NEWS.md b/NEWS.md
index ab08c1ee76..5ac57ea155 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -8,6 +8,13 @@ See the end of the file for license conditions.
 
 ### From 2.19.1 to 2.20.0
 
+#### Deprecations
+
+- Calling `-zip` with two arguments now emits a warning.  This
+  long-discouraged calling convention remains supported, but the
+  caller is now referred to the equivalent `-zip-pair` instead (Stefan
+  Monnier, #400).
+
 #### Fixes
 
 - Fixed a regression from `2.18` in `-take` that caused it to
@@ -132,8 +139,8 @@ 
https://github.com/magnars/dash.el/wiki/Obsoletion-of-dash-functional.el
 - Sped up `-uniq` by using hash-tables when possible (@cireu, #305).
 - Fixed `-inits` to be non-destructive (@SwiftLawnGnome, #313).
 - Fixed indent rules for `-some->` and family (@wbolster, #321).
-- Added `-zip-lists` which always returns a list of proper lists, even for two
-  input lists (see issue #135).
+- Added `-zip-lists` which always returns a list of proper lists, even
+  for two input lists, in contrast to `-zip` (see issue #135).
 
 ### From 2.15 to 2.16
 
diff --git a/dash.el b/dash.el
index 375b4cdc93..ef4f9d3263 100644
--- a/dash.el
+++ b/dash.el
@@ -536,8 +536,9 @@ This function's anaphoric counterpart is `--remove-first'.
 See also `-map-first', `-remove-item', and `-remove-last'."
   (--remove-first (funcall pred it) list))
 
+;; TODO: #'-quoting the macro upsets Emacs 24.
 (defalias '-reject-first #'-remove-first)
-(defalias '--reject-first #'--remove-first)
+(defalias '--reject-first '--remove-first)
 
 (defmacro --remove-last (form list)
   "Remove the last item from LIST for which FORM evals to non-nil.
@@ -1298,28 +1299,41 @@ See also: `-map-when'"
   `(-update-at ,n (lambda (it) (ignore it) ,form) ,list))
 
 (defun -remove-at (n list)
-  "Return a list with element at Nth position in LIST removed.
+  "Return LIST with its element at index N removed.
+That is, remove any element selected as (nth N LIST) from LIST
+and return the result.
 
-See also: `-remove-at-indices', `-remove'"
+This is a non-destructive operation: parts of LIST (but not
+necessarily all of it) are copied as needed to avoid
+destructively modifying it.
+
+See also: `-remove-at-indices', `-remove'."
   (declare (pure t) (side-effect-free t))
-  (-remove-at-indices (list n) list))
+  (if (zerop n)
+      (cdr list)
+    (--remove-first (= it-index n) list)))
 
 (defun -remove-at-indices (indices list)
-  "Return a list whose elements are elements from LIST without
-elements selected as `(nth i list)` for all i
-from INDICES.
+  "Return LIST with its elements at INDICES removed.
+That is, for each index I in INDICES, remove any element selected
+as (nth I LIST) from LIST.
 
-See also: `-remove-at', `-remove'"
+This is a non-destructive operation: parts of LIST (but not
+necessarily all of it) are copied as needed to avoid
+destructively modifying it.
+
+See also: `-remove-at', `-remove'."
   (declare (pure t) (side-effect-free t))
-  (let* ((indices (-sort '< indices))
-         (diffs (cons (car indices) (-map '1- (-zip-with '- (cdr indices) 
indices))))
-         r)
-    (--each diffs
-      (let ((split (-split-at it list)))
-        (!cons (car split) r)
-        (setq list (cdr (cadr split)))))
-    (!cons list r)
-    (apply #'-concat (nreverse r))))
+  (setq indices (--drop-while (< it 0) (-sort #'< indices)))
+  (let ((i (pop indices)) res)
+    (--each-while list i
+      (pop list)
+      (if (/= it-index i)
+          (push it res)
+        (while (and indices (= (car indices) i))
+          (pop indices))
+        (setq i (pop indices))))
+    (nconc (nreverse res) list)))
 
 (defmacro --split-with (pred list)
   "Anaphoric form of `-split-with'."
@@ -1603,115 +1617,192 @@ elements of LIST.  Keys are compared by `equal'."
       (nreverse result))))
 
 (defmacro --zip-with (form list1 list2)
-  "Anaphoric form of `-zip-with'.
+  "Zip LIST1 and LIST2 into a new list according to FORM.
+That is, evaluate FORM for each item pair from the two lists, and
+return the list of results.  The result is as long as the shorter
+list.
+
+Each element of LIST1 and each element of LIST2 in turn are bound
+pairwise to `it' and `other', respectively, and their index
+within the list to `it-index', before evaluating FORM.
 
-Each element in turn of LIST1 is bound to `it', and of LIST2 to
-`other', before evaluating FORM."
+This is the anaphoric counterpart to `-zip-with'."
   (declare (debug (form form form)))
   (let ((r (make-symbol "result"))
-        (l1 (make-symbol "list1"))
         (l2 (make-symbol "list2")))
-    `(let ((,r nil)
-           (,l1 ,list1)
-           (,l2 ,list2))
-       (while (and ,l1 ,l2)
-         (let ((it (car ,l1))
-               (other (car ,l2)))
-           (!cons ,form ,r)
-           (!cdr ,l1)
-           (!cdr ,l2)))
+    `(let ((,l2 ,list2) ,r)
+       (--each-while ,list1 ,l2
+         (let ((other (pop ,l2)))
+           (ignore other)
+           (push ,form ,r)))
        (nreverse ,r))))
 
 (defun -zip-with (fn list1 list2)
-  "Zip the two lists LIST1 and LIST2 using a function FN.  This
-function is applied pairwise taking as first argument element of
-LIST1 and as second argument element of LIST2 at corresponding
-position.
+  "Zip LIST1 and LIST2 into a new list using the function FN.
+That is, apply FN pairwise taking as first argument the next
+element of LIST1 and as second argument the next element of LIST2
+at the corresponding position.  The result is as long as the
+shorter list.
 
-The anaphoric form `--zip-with' binds the elements from LIST1 as symbol `it',
-and the elements from LIST2 as symbol `other'."
+This function's anaphoric counterpart is `--zip-with'.
+
+For other zips, see also `-zip-lists' and `-zip-fill'."
   (--zip-with (funcall fn it other) list1 list2))
 
 (defun -zip-lists (&rest lists)
-  "Zip LISTS together.  Group the head of each list, followed by the
-second elements of each list, and so on. The lengths of the returned
-groupings are equal to the length of the shortest input list.
+  "Zip LISTS together.
+
+Group the head of each list, followed by the second element of
+each list, and so on.  The number of returned groupings is equal
+to the length of the shortest input list, and the length of each
+grouping is equal to the number of input LISTS.
 
-The return value is always list of lists, which is a difference
-from `-zip-pair' which returns a cons-cell in case two input
-lists are provided.
+The return value is always a list of proper lists, in contrast to
+`-zip' which returns a list of dotted pairs when only two input
+LISTS are provided.
 
-See also: `-zip'"
+See also: `-zip-pair'."
   (declare (pure t) (side-effect-free t))
   (when lists
     (let (results)
-      (while (-none? 'null lists)
-        (setq results (cons (mapcar 'car lists) results))
-        (setq lists (mapcar 'cdr lists)))
+      (while (--every it lists)
+        (push (mapcar #'car lists) results)
+        (setq lists (mapcar #'cdr lists)))
       (nreverse results))))
 
+(defun -zip-lists-fill (fill-value &rest lists)
+  "Zip LISTS together, padding shorter lists with FILL-VALUE.
+This is like `-zip-lists' (which see), except it retains all
+elements at positions beyond the end of the shortest list.  The
+number of returned groupings is equal to the length of the
+longest input list, and the length of each grouping is equal to
+the number of input LISTS."
+  (declare (pure t) (side-effect-free t))
+  (when lists
+    (let (results)
+      (while (--some it lists)
+        (push (--map (if it (car it) fill-value) lists) results)
+        (setq lists (mapcar #'cdr lists)))
+      (nreverse results))))
+
+(defun -unzip-lists (lists)
+  "Unzip LISTS.
+
+This works just like `-zip-lists' (which see), but takes a list
+of lists instead of a variable number of arguments, such that
+
+  (-unzip-lists (-zip-lists ARGS...))
+
+is identity (given that the lists comprising ARGS are of the same
+length)."
+  (declare (pure t) (side-effect-free t))
+  (apply #'-zip-lists lists))
+
+(defalias 'dash--length=
+  (if (fboundp 'length=)
+      #'length=
+    (lambda (list length)
+      (cond ((< length 0) nil)
+            ((zerop length) (null list))
+            ((let ((last (nthcdr (1- length) list)))
+               (and last (null (cdr last))))))))
+  "Return non-nil if LIST is of LENGTH.
+This is a compatibility shim for `length=' in Emacs 28.
+\n(fn LIST LENGTH)")
+
+(defun dash--zip-lists-or-pair (_form &rest lists)
+  "Return a form equivalent to applying `-zip' to LISTS.
+This `compiler-macro' warns about discouraged `-zip' usage and
+delegates to `-zip-lists' or `-zip-pair' depending on the number
+of LISTS."
+  (if (not (dash--length= lists 2))
+      (cons #'-zip-lists lists)
+    (let ((pair (cons #'-zip-pair lists))
+          (msg "Use -zip-pair instead of -zip to get a list of pairs"))
+      (if (fboundp 'macroexp-warn-and-return)
+          (macroexp-warn-and-return msg pair)
+        (message msg)
+        pair))))
+
 (defun -zip (&rest lists)
-  "Zip LISTS together.  Group the head of each list, followed by the
-second elements of each list, and so on. The lengths of the returned
-groupings are equal to the length of the shortest input list.
-
-If two lists are provided as arguments, return the groupings as a list
-of cons cells. Otherwise, return the groupings as a list of lists.
-
-Use `-zip-lists' if you need the return value to always be a list
-of lists.
-
-Alias: `-zip-pair'
-
-See also: `-zip-lists'"
-  (declare (pure t) (side-effect-free t)
-           (compiler-macro
-            (lambda (form)
-              (if (not (= 2 (length lists)))
-                  (cons #'-zip-lists lists)
-                (let ((msg "Use -zip-pair to get a list of pairs"))
-                  (if (fboundp 'macroexp-warn-and-return)
-                      (macroexp-warn-and-return
-                       msg (cons #'-zip-pair lists))
-                    (message msg)
-                    form))))))
-  ;; To support backward compatibility, return
-  ;; cons cells if two lists were provided.
-  (apply (if (= (length lists) 2) #'-zip-pair #'-zip-lists) lists))
-
-(defun -zip-pair (list1 list2)
+  "Zip LISTS together.
+
+Group the head of each list, followed by the second element of
+each list, and so on.  The number of returned groupings is equal
+to the length of the shortest input list, and the number of items
+in each grouping is equal to the number of input LISTS.
+
+If only two LISTS are provided as arguments, return the groupings
+as a list of dotted pairs.  Otherwise, return the groupings as a
+list of proper lists.
+
+Since the return value changes form depending on the number of
+arguments, it is generally recommended to use `-zip-lists'
+instead, or `-zip-pair' if a list of dotted pairs is desired.
+
+See also: `-unzip'."
+  (declare (compiler-macro dash--zip-lists-or-pair)
+           (pure t) (side-effect-free t))
+  ;; For backward compatibility, return a list of dotted pairs if two
+  ;; arguments were provided.
+  (apply (if (dash--length= lists 2) #'-zip-pair #'-zip-lists) lists))
+
+(defun -zip-pair (&rest lists)
   "Zip LIST1 and LIST2 together.
-Make a pair of the head of each list, followed by the
-second elements of each list, and so on.  The returned
-groupings are equal to the length of the shortest input list.
 
-See also: `-zip-lists'"
-  (--map (cons (car it) (cadr it))  (-zip-lists list1 list2)))
+Make a pair with the head of each list, followed by a pair with
+the second element of each list, and so on.  The number of pairs
+returned is equal to the length of the shorter input list.
+
+See also: `-zip-lists'."
+  (declare (advertised-calling-convention (list1 list2) "2.20.0")
+           (pure t) (side-effect-free t))
+  (if (dash--length= lists 2)
+      (--zip-with (cons it other) (car lists) (cadr lists))
+    (apply #'-zip-lists lists)))
 
 (defun -zip-fill (fill-value &rest lists)
-  "Zip LISTS, with FILL-VALUE padded onto the shorter lists. The
-lengths of the returned groupings are equal to the length of the
-longest input list."
+  "Zip LISTS together, padding shorter lists with FILL-VALUE.
+This is like `-zip' (which see), except it retains all elements
+at positions beyond the end of the shortest list.  The number of
+returned groupings is equal to the length of the longest input
+list, and the length of each grouping is equal to the number of
+input LISTS.
+
+Since the return value changes form depending on the number of
+arguments, it is generally recommended to use `-zip-lists-fill'
+instead, unless a list of dotted pairs is explicitly desired."
   (declare (pure t) (side-effect-free t))
-  (apply #'-zip (apply #'-pad (cons fill-value lists))))
+  (cond ((null lists) ())
+        ((dash--length= lists 2)
+         (let ((list1 (car lists))
+               (list2 (cadr lists))
+               results)
+           (while (or list1 list2)
+             (push (cons (if list1 (pop list1) fill-value)
+                         (if list2 (pop list2) fill-value))
+                   results))
+           (nreverse results)))
+        ((apply #'-zip-lists-fill fill-value lists))))
 
 (defun -unzip (lists)
   "Unzip LISTS.
 
-This works just like `-zip' but takes a list of lists instead of
-a variable number of arguments, such that
+This works just like `-zip' (which see), but takes a list of
+lists instead of a variable number of arguments, such that
 
   (-unzip (-zip L1 L2 L3 ...))
 
-is identity (given that the lists are the same length).
+is identity (given that the lists are of the same length, and
+that `-zip' is not called with two arguments, because of the
+caveat described in its docstring).
 
-Note in particular that calling this on a list of two lists will
-return a list of cons-cells such that the above identity works.
+Note in particular that calling `-unzip' on a list of two lists
+will return a list of dotted pairs.
 
-See also: `-zip'"
-  ;; FIXME: Huh?  AFAIK `-zip' is not its own inverse.
-  ;; (-unzip (-zip '(1 2 3) '(a b c)))
-  ;; gives me "Wrong type argument: listp, a"
+Since the return value changes form depending on the number of
+LISTS, it is generally recommended to use `-unzip-lists' instead."
+  (declare (pure t) (side-effect-free t))
   (apply #'-zip lists))
 
 (defun -cycle (list)
@@ -2228,7 +2319,7 @@ This method normalizes PATTERN to the format expected by
   (let ((normalized (list (car pattern)))
         (skip nil)
         (fill-placeholder (make-symbol "--dash-fill-placeholder--")))
-    (-each (apply #'-zip (-pad fill-placeholder (cdr pattern) (cddr pattern)))
+    (-each (-zip-fill fill-placeholder (cdr pattern) (cddr pattern))
       (lambda (pair)
         (let ((current (car pair))
               (next (cdr pair)))
@@ -2567,7 +2658,8 @@ because we need to support improper list binding."
          ,@body)
     (let* ((varlist (dash--normalize-let-varlist varlist))
            (inputs (--map-indexed (list (make-symbol (format "input%d" 
it-index)) (cadr it)) varlist))
-           (new-varlist (--map (list (caar it) (cadr it)) (-zip-pair varlist 
inputs))))
+           (new-varlist (--zip-with (list (car it) (car other))
+                                    varlist inputs)))
       `(let ,inputs
          (-let* ,new-varlist ,@body)))))
 
@@ -3742,7 +3834,8 @@ This function satisfies the following laws:
     (-compose (-partial #\\='nth n)
               (-prod f1 f2 ...))
   = (-compose fn (-partial #\\='nth n))"
-  (lambda (x) (-zip-with 'funcall fns x)))
+  (declare (pure t) (side-effect-free t))
+  (lambda (x) (--zip-with (funcall it other) fns x)))
 
 ;;; Font lock
 
@@ -3868,7 +3961,8 @@ which do not dynamically detect macros, Dash-Fontify mode
 additionally fontifies Dash macro calls.
 
 See also `dash-fontify-mode-lighter' and
-`global-dash-fontify-mode'." :lighter dash-fontify-mode-lighter
+`global-dash-fontify-mode'."
+  :lighter dash-fontify-mode-lighter
   (if dash-fontify-mode
       (font-lock-add-keywords nil dash--keywords t)
     (font-lock-remove-keywords nil dash--keywords))
diff --git a/readme-template.md b/readme-template.md
index 3065d2c80a..ef33241438 100644
--- a/readme-template.md
+++ b/readme-template.md
@@ -14,7 +14,6 @@ See the end of the file for license conditions.
 ## Contents
 
 * [Change log](#change-log)
-  * [Upcoming breaking change!](#upcoming-breaking-change)
 * [Installation](#installation)
 * [Functions](#functions)
 * [Contribute](#contribute)
@@ -25,18 +24,6 @@ See the end of the file for license conditions.
 
 See the [`NEWS.md`](NEWS.md) file.
 
-### Upcoming breaking change!
-
-- For backward compatibility reasons, `-zip` when called with two
-  lists returns a list of cons cells, rather than a list of proper
-  lists.  This is a clunky API, and may be changed in a future release
-  to always return a list of proper lists, as `-zip-lists` currently
-  does.
-
-  **N.B.:** Do not rely on the current behavior of `-zip` for two
-  lists.  Instead, use `-zip-pair` for a list of cons cells, and
-  `-zip-lists` for a list of proper lists.
-
 ## Installation
 
 Dash is available on [GNU ELPA](https://elpa.gnu.org/), [GNU-devel



reply via email to

[Prev in Thread] Current Thread [Next in Thread]