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))