[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.