From 12f63c18f6d5a886f62f10b4c8de8de3509e52df Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Wed, 28 Sep 2022 13:19:08 +0200 Subject: [PATCH] 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 ee2acdb002b..9635b335bca 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 b85975944a0..e70f9be5468 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 26fba4771bc..2a8fc46a9f9 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. -- 2.39.2