emacs-diffs
[Top][All Lists]
Advanced

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

master 12f63c18f6 1/2: Add new macro 'while-let'


From: Lars Ingebrigtsen
Subject: master 12f63c18f6 1/2: Add new macro 'while-let'
Date: Wed, 28 Sep 2022 07:28:14 -0400 (EDT)

branch: master
commit 12f63c18f6d5a886f62f10b4c8de8de3509e52df
Author: Lars Ingebrigtsen <larsi@gnus.org>
Commit: Lars Ingebrigtsen <larsi@gnus.org>

    Add new macro 'while-let'
    
    * doc/lispref/control.texi (Conditionals): Document
    when-let/if-let/while-let.
    * lisp/subr.el (while-let): New macro.
---
 doc/lispref/control.texi | 42 ++++++++++++++++++++++++++++++++++++++++++
 etc/NEWS                 |  4 ++++
 lisp/subr.el             | 13 +++++++++++++
 3 files changed, 59 insertions(+)

diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi
index ee2acdb002..9635b335bc 100644
--- a/doc/lispref/control.texi
+++ b/doc/lispref/control.texi
@@ -294,6 +294,48 @@ For example:
 @end group
 @end example
 
+If can be convenient to bind variables in conjunction with using a
+conditional.  It's often the case that you do a computation, and then
+want to do something with that computation if it's non-@code{nil}.
+The straightforward way to do that is to just write, for instance:
+
+@example
+(let ((result1 (do-computation)))
+  (when result1
+    (let ((result2 (do-more result1)))
+      (when result2
+        (do-something result2)))))
+@end example
+
+Since this is a very common pattern, Emacs provides a number of macros
+to make this easier and more readable.  The above can be written the
+following way instead:
+
+@example
+(when-let ((result1 (do-computation))
+           (result2 (do-more result1)))
+  (do-something result2))
+@end example
+
+There's a number of variations on this theme, and they're briefly
+described below.
+
+@defmac if-let spec then-form else-forms...
+Evaluate each binding in @var{spec} in turn, like in @code{let*}
+(@pxref{Local Variables}, stopping if a binding value is @code{nil}.
+If all are non-@code{nil}, return the value of @var{then-form},
+otherwise the last form in @var{else-forms}.
+@end defmac
+
+@defmac when-let spec then-forms...
+Like @code{if-let}, but without @var{else-forms}.
+@end defmac
+
+@defmac while-let spec then-forms...
+Like @code{when-let}, but repeat until a binding in @var{spec} is
+@code{nil}.  The return value is always @code{nil}.
+@end defmac
+
 @node Combining Conditions
 @section Constructs for Combining Conditions
 @cindex combining conditions
diff --git a/etc/NEWS b/etc/NEWS
index b85975944a..e70f9be546 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -3012,6 +3012,10 @@ The following generalized variables have been made 
obsolete:
 
 * Lisp Changes in Emacs 29.1
 
++++
+** New macro 'while-let'.
+This is like 'when-let', but repeats until a binding form is nil.
+
 +++
 ** New function 'make-obsolete-generalized-variable'.
 This can be used to mark setters used by 'setf' as obsolete, and the
diff --git a/lisp/subr.el b/lisp/subr.el
index 26fba4771b..2a8fc46a9f 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -2514,7 +2514,20 @@ The variable list SPEC is the same as in `if-let'."
   (declare (indent 1) (debug if-let))
   (list 'if-let spec (macroexp-progn body)))
 
+(defmacro while-let (spec &rest body)
+  "Bind variables according to SPEC and conditionally evaluate BODY.
+Evaluate each binding in turn, stopping if a binding value is nil.
+If all bindings are non-nil, eval BODY and repeat.
 
+The variable list SPEC is the same as in `if-let'."
+  (declare (indent 1) (debug if-let))
+  (let ((done (gensym "done")))
+    `(catch ',done
+       (while t
+         (if-let ,spec
+             (progn
+               ,@body)
+           (throw ',done nil))))))
 
 ;; PUBLIC: find if the current mode derives from another.
 



reply via email to

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