branch: elpa/loopy
commit 9b6b5c09849c12142dc8b7088202db1093588908
Author: okamsn <28612288+oka...@users.noreply.github.com>
Commit: GitHub <nore...@github.com>

    Deprecate `loopy-default-flags`. (#245)
    
    This is needed for better supporting libraries wanting to use the macro in
    different ways.  We can't have one library globally change the settings
    for another library.  See issue #231.
    
    In the Org doc, show how to write a wrapping macro that defaults to using a
    certain flag.
    
    - Update the Org documentation to remove references to 
`loopy-default-flags`.
    - Message a warning when `loopy-default-flags` is modified.
    - Message a warning when `loopy-default-flags` is found to be non-`nil` 
during
      macro expansion of `loopy` and `loopy-iter`.
    - Mark `loopy-default-flags` as an obsolete variable.
---
 CHANGELOG.md       | 38 ++++++++++++++++++++++++++++++++++
 README.org         |  3 +++
 doc/loopy-doc.org  | 61 +++++++++++++++++++++++++++++++++++++++---------------
 doc/loopy.texi     | 53 +++++++++++++++++++++++++++++++++++++++--------
 lisp/loopy-iter.el |  7 ++++++-
 lisp/loopy-vars.el |  8 +++++++
 lisp/loopy.el      |  8 ++++++-
 7 files changed, 150 insertions(+), 28 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30532191e96..cc8cdaf9430 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -79,6 +79,43 @@ For Loopy Dash, see <https://github.com/okamsn/loopy-dash>.
          (finally-do (push 0 loopy-result)))
   ```
 
+- `loopy-default-flags` is now deprecated ([#245]).  This prevents one library
+  from breaking the macro expansions in another library.  Instead of using
+  `loopy-default-flags`, one should use a wrapping macro like the one below
+  (copied from the Info documentation).
+
+  ```emacs-lisp
+  (require 'loopy-pcase)
+  (defmacro my-loopy-flag-wrapper (&rest body)
+    "Use `loopy', but default to `pcase' destructuring."
+    (loopy (with (entry-for-flag (map-elt loopy-parsers 'flag)))
+           (list arg body)
+           (if (and (consp arg)
+                    (eq (map-elt loopy-parsers (car arg))
+                        entry-for-flag))
+               (command-do
+                (set flag-found t)
+                (collect (append arg '(pcase))))
+             (collect arg))
+           (finally-do
+            (unless flag-found
+              (push '(flag pcase) loopy-result))
+            (push 'loopy loopy-result))))
+
+  ;; => (1 2 3 4)
+  (my-loopy-flag-wrapper (list `(,i . ,j) '((1 . 2) (3 . 4)))
+                         (collect i)
+                         (collect j))
+
+  ;; Ignores the `seq' flag as expected:
+  ;;
+  ;; => ( 1 2 3 4)
+  (my-loopy-flag-wrapper (flag seq)
+                         (list `(,i . ,j) '((1 . 2) (3 . 4)))
+                         (collect i)
+                         (collect j))
+  ```
+
 ### Internal Changes
 
 - As far as the implementation is concerned, "aliases" are no longer a separate
@@ -100,6 +137,7 @@ For Loopy Dash, see <https://github.com/okamsn/loopy-dash>.
 [#242]: https://github.com/okamsn/loopy/PR/242
 [#243]: https://github.com/okamsn/loopy/PR/243
 [#244]: https://github.com/okamsn/loopy/PR/244
+[#245]: https://github.com/okamsn/loopy/PR/245
 [#246]: https://github.com/okamsn/loopy/PR/246
 
 ## 0.14.0
diff --git a/README.org b/README.org
index 8a3582b0c6e..1a74bf5ef29 100644
--- a/README.org
+++ b/README.org
@@ -49,6 +49,9 @@ please let me know.
      commands are aliased.
    - Modifications to an implied ~loopy-result~ in =finally-do= will now be
      included in the implied return value of the macro.
+   - ~loopy-default-flags~ is made obsolete to avoid conflicts between
+     libraries.  Using a wrapping macro instead, such as the one given in
+     Changelog or the Org/Info documentation.
  - Version 0.14.0:
    - Conflicting initialization values for accumulation variables now signal
      a warning.  In the future, they will signal an error.
diff --git a/doc/loopy-doc.org b/doc/loopy-doc.org
index 7f93f9df4c0..8f893b99ba6 100644
--- a/doc/loopy-doc.org
+++ b/doc/loopy-doc.org
@@ -4901,11 +4901,6 @@ method).
 Flags are applied in order.  If you specify =(flags seq pcase)=, then ~loopy~
 will use ~pcase-let~ for destructuring, not ~seq-let~.
 
-#+vindex: loopy-default-flags
-If you wish to always use a flag, you can add that flag to the list
-~loopy-default-flags~.  These can be overridden by any flag given in the =flag=
-special macro argument.
-
 The following flags are currently supported:
 
 #+cindex: pcase flag
@@ -4923,15 +4918,12 @@ The following flags are currently supported:
 For convenience, all flags (except =default=) can be undone by prefixing them
 with =-= (a dash or minus sign), which reverts ~loopy~ to its default behavior.
 
-For example, if you have set ~loopy-default-flags~ to =(dash)= and wish to use
-the default destructuring method, you can use =(flags default)= or =(flags
--dash)=.  These prefixed flags only apply when the unprefixed version is 
active.
-That is, =(flags pcase -dash)= is the same as just =(flags pcase)=, regardless
-of the value of ~loopy-default-flags~, as =pcase= destructuring will override
-all uses of =dash= destructuring as it comes later in the list.  Similarly,
-=(flags -dash dash)= and =(flags -dash +dash)= leave =dash= destructuring
-enabled, and =(flags +dash -dash)= disables =dash= destructuring and uses the
-default behavior.
+These prefixed flags only apply when the unprefixed version is active.  That 
is,
+=(flags pcase -dash)= is the same as just =(flags pcase)=, as =pcase=
+destructuring will override all uses of =dash= destructuring as it comes later
+in the list.  Similarly, =(flags -dash dash)= and =(flags -dash +dash)= leave
+=dash= destructuring enabled, and =(flags +dash -dash)= disables =dash=
+destructuring and uses the default behavior.
 
 #+cindex: loopy-dash
 #+cindex: loopy-pcase
@@ -4982,7 +4974,6 @@ yet provide the required functionality.
          (finally-return (+ sum1 v1) (+ sum2 v2)))
 #+end_src
 
-
 #+attr_texinfo: :tag Warning
 #+begin_quote
 For accumulation commands, there is no guarantee that a variable that was used
@@ -4991,14 +4982,12 @@ new variables as they please, which can be interpreted 
as accumulation
 variables.
 #+end_quote
 
-
 Consider the below example in which a hypothetical ~pcase~ pattern creates the
 variable ~temporary?~ for destructuring.  Loopy has no way of knowing whether 
it
 was the user who create the variable, or the destructuring system.  As a 
result,
 ~temporary?~ is treated as an accumulation variable.  Such cases can be 
unwanted
 and produce inefficient code.
 
-
 #+begin_src emacs-lisp
   ;; Possibly unexpected behavior:
   ;;
@@ -5011,6 +5000,44 @@ and produce inefficient code.
          (finally-return whole temporary?))
 #+end_src
 
+If you wish to always use one of the destructuring flags, you can use a 
wrapping
+macro around ~loopy~ or ~loopy-iter~.  The =flag= special macro argument is
+identified by its entry in the customizable variable ~loopy-parsers~.
+Therefore, uses of =flag=, including aliases, can be identified by checking
+~loopy-parsers~ (so long as the entry for =flag= itself has not been changed).
+
+#+begin_src emacs-lisp
+  (require 'loopy-pcase)
+  (defmacro my-loopy-flag-wrapper (&rest body)
+    "Use `loopy', but default to `pcase' destructuring."
+    (loopy (with (entry-for-flag (map-elt loopy-parsers 'flag)))
+           (list arg body)
+           (if (and (consp arg)
+                    (eq (map-elt loopy-parsers (car arg))
+                        entry-for-flag))
+               (command-do
+                (set flag-found t)
+                (collect (append arg '(pcase))))
+             (collect arg))
+           (finally-do
+            (unless flag-found
+              (push '(flag pcase) loopy-result))
+            (push 'loopy loopy-result))))
+
+  ;; => (1 2 3 4)
+  (my-loopy-flag-wrapper (list `(,i . ,j) '((1 . 2) (3 . 4)))
+                         (collect i)
+                         (collect j))
+
+  ;; Ignores the `seq' flag as expected:
+  ;;
+  ;; => ( 1 2 3 4)
+  (my-loopy-flag-wrapper (flag seq)
+                         (list `(,i . ,j) '((1 . 2) (3 . 4)))
+                         (collect i)
+                         (collect j))
+#+end_src
+
 ** Custom Aliases
 :PROPERTIES:
 :CUSTOM_ID: custom-aliases
diff --git a/doc/loopy.texi b/doc/loopy.texi
index 99f357d1976..7a0b4520529 100644
--- a/doc/loopy.texi
+++ b/doc/loopy.texi
@@ -5442,15 +5442,12 @@ Use the default behavior for all options.
 For convenience, all flags (except @samp{default}) can be undone by prefixing 
them
 with @samp{-} (a dash or minus sign), which reverts @code{loopy} to its 
default behavior.
 
-For example, if you have set @code{loopy-default-flags} to @samp{(dash)} and 
wish to use
-the default destructuring method, you can use @samp{(flags default)} or 
@samp{(flags
--dash)}.  These prefixed flags only apply when the unprefixed version is 
active.
-That is, @samp{(flags pcase -dash)} is the same as just @samp{(flags pcase)}, 
regardless
-of the value of @code{loopy-default-flags}, as @samp{pcase} destructuring will 
override
-all uses of @samp{dash} destructuring as it comes later in the list.  
Similarly,
-@samp{(flags -dash dash)} and @samp{(flags -dash +dash)} leave @samp{dash} 
destructuring
-enabled, and @samp{(flags +dash -dash)} disables @samp{dash} destructuring and 
uses the
-default behavior.
+These prefixed flags only apply when the unprefixed version is active.  That 
is,
+@samp{(flags pcase -dash)} is the same as just @samp{(flags pcase)}, as 
@samp{pcase}
+destructuring will override all uses of @samp{dash} destructuring as it comes 
later
+in the list.  Similarly, @samp{(flags -dash dash)} and @samp{(flags -dash 
+dash)} leave
+@samp{dash} destructuring enabled, and @samp{(flags +dash -dash)} disables 
@samp{dash}
+destructuring and uses the default behavior.
 
 @cindex loopy-dash
 @cindex loopy-pcase
@@ -5507,6 +5504,44 @@ yet provide the required functionality.
        (finally-return (+ sum1 v1) (+ sum2 v2)))
 @end lisp
 
+If you wish to always use one of the destructuring flags, you can use a 
wrapping
+macro around @code{loopy} or @code{loopy-iter}.  The @samp{flag} special macro 
argument is
+identified by its entry in the customizable variable @code{loopy-parsers}.  
Hence,
+uses of @samp{flag}, including aliases, can be identified by checking 
@code{loopy-parsers}
+(so long as the entry for @samp{flag} itself has not been changed).
+
+@lisp
+(require 'loopy-pcase)
+(defmacro my-loopy-flag-wrapper (&rest body)
+  "Use `loopy', but default to `pcase' destructuring."
+  (loopy (with (flag-found nil))
+         (list arg body)
+         (collect (if (and (consp arg)
+                           (eq (map-elt loopy-parsers (car arg))
+                               (map-elt loopy-parsers 'flag)))
+                      (progn
+                        (setq flag-found t)
+                        (append arg '(pcase)))
+                    arg))
+         (finally-return
+          `(loopy ,@@(unless flag-found
+                      '((flag pcase)))
+                  ,@@loopy-result))))
+
+;; => (1 2 3 4)
+(my-loopy-flag-wrapper (list `(,i . ,j) '((1 . 2) (3 . 4)))
+                       (collect i)
+                       (collect j))
+
+;; Ignores the `seq' flag as expected:
+;;
+;; => ( 1 2 3 4)
+(my-loopy-flag-wrapper (flag seq)
+                       (list `(,i . ,j) '((1 . 2) (3 . 4)))
+                       (collect i)
+                       (collect j))
+@end lisp
+
 
 @quotation Warning
 For accumulation commands, there is no guarantee that a variable that was used
diff --git a/lisp/loopy-iter.el b/lisp/loopy-iter.el
index 20217a6d9fc..237bd4484b0 100644
--- a/lisp/loopy-iter.el
+++ b/lisp/loopy-iter.el
@@ -551,7 +551,12 @@ to use `loopy' in general.
      (setq loopy-iter--keywords-internal loopy-iter-keywords
            loopy-iter--bare-names-internal loopy-iter-bare-names)
 
-     (mapc #'loopy--apply-flag loopy-default-flags)
+     (when loopy-default-flags
+       (warn "`loopy-default-flags' is obsolete.  Use a wrapping macro.
+This is necessary to better support using the macro in different
+packages from different authors.  See the updated Info node
+`(loopy)Customizing Macro Behavior'.")
+       (mapc #'loopy--apply-flag loopy-default-flags))
 
      (setq body (thread-first body
                               loopy-iter--process-special-arg-override
diff --git a/lisp/loopy-vars.el b/lisp/loopy-vars.el
index 4501cb10303..68cf5b8df5a 100644
--- a/lisp/loopy-vars.el
+++ b/lisp/loopy-vars.el
@@ -43,6 +43,14 @@
   :prefix "loopy-"
   :link '(url-link "https://github.com/okamsn/loopy";))
 
+(make-obsolete-variable
+ 'loopy-default-flags
+ "Use a wrapping macro.
+This is necessary to better support using the macro in different
+packages from different authors.  See the updated Info node
+`(loopy)Customizing Macro Behavior'."
+ "2025-10"
+ 'set)
 ;;;###autoload
 (defcustom loopy-default-flags nil
   "Which flags should alter the behavior of `loopy' by default.
diff --git a/lisp/loopy.el b/lisp/loopy.el
index bcc7495b23b..5ea943eddd1 100644
--- a/lisp/loopy.el
+++ b/lisp/loopy.el
@@ -953,7 +953,13 @@ see the Info node `(loopy)' distributed with this package."
              (puthash alias parser loopy--parsers-internal))))))
 
 ;;;;; Process the special macro arguments.
-   (mapc #'loopy--apply-flag loopy-default-flags)
+   (when loopy-default-flags
+     (warn "`loopy-default-flags' is obsolete.  Use a wrapping macro.
+This is necessary to better support using the macro in different
+packages from different authors.  See the updated Info node
+`(loopy)Customizing Macro Behavior'.")
+     (mapc #'loopy--apply-flag loopy-default-flags))
+
    (setq body (loopy--process-special-arg-override body))
    (setq body (loopy--process-special-arg-loop-name body))
    (setq body (loopy--process-special-arg-flag body))

Reply via email to