diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index fc0a56ee6d5..b7b2d40caee 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi @@ -317,8 +317,8 @@ following way instead: (do-something result2)) @end example -There's a number of variations on this theme, and they're briefly -described below. +There's a number of variations on this theme, and they're described +below. @defmac if-let* varlist then-form else-forms... Evaluate each binding in @var{varlist}, stopping if a binding value is @@ -376,6 +376,22 @@ Some Lisp programmers follow the convention that @code{and} and @code{when} and @code{when-let*} are for forms evaluated for side-effect with returned values ignored. +There is no @code{cond-let*} macro because extending the structure +shared by @code{if-let*}, @code{when-let*} and @code{and-let*} to the +case of @code{cond} is not simple.@footnote{The problem is that there +are multiple ways to do it that are all useful but not compatible. In +addition, the resulting macro is complicated, and so tricky to learn how +to read and write.} However, you can use the @code{cond*} macro's +@code{bind-and*} clauses (@pxref{cond* Macro}) to achieve something +similar: + +@example +(cond* ((bind-and* (result1 (do-computation)) + (result2 (do-more result1))) + (do-something result2)) + ...) +@end example + A similar macro exists to run a loop until one binding evaluates to @code{nil}: @@ -1503,6 +1519,14 @@ the bindings list in @code{let*}, @pxref{Local Variables}) for the body of the clause, and all subsequent clauses. As a condition, it counts as true if the first binding's value is non-@code{nil}. +@findex bind-and* +@code{(bind-and* @var{bindings}@dots{})} means to bind @var{bindings} +(like the bindings list in @code{if-let*}, @pxref{Conditionals}) for +only the body of the clause. As a condition, it counts as true if none +of the bindings evaluate to @code{nil}. In addition, if any binding +evaluates to @code{nil}, the expressions for the values of subsequent +bindings are not evaluated. + @findex match* @findex pcase* @code{(match* @var{pattern} @var{datum})} means to match @var{datum} diff --git a/etc/NEWS b/etc/NEWS index 7345c08218b..ed504118e66 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3049,6 +3049,16 @@ Other functions that use 'aset' to modify string data, such as 'subst-char-in-string' with a non-nil INPLACE argument, will signal an error if called with arguments that would violate these rules. ++++ +** More program constants are combined by the compiler. +The compiler now unifies more constants that are 'equal' for better code +generation. This does not affect correct programs but may expose some +coding mistakes. For example, + + (eq (cdr '(1 2 3)) '(2 3))) + +may return either nil or t. + ** Nested backquotes are not supported any more in Pcase patterns. --- diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index bf3d80f2d80..b5b371f7833 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -3870,8 +3870,7 @@ assignment (i.e. `setq')." (byte-compile-dynamic-variable-op 'byte-varset var)))) (defmacro byte-compile-get-constant (const) - `(or (assoc ,const byte-compile-constants - (if (stringp ,const) #'equal-including-properties #'eql)) + `(or (assoc ,const byte-compile-constants #'equal-including-properties) (car (setq byte-compile-constants (cons (list ,const) byte-compile-constants))))) diff --git a/lisp/subr.el b/lisp/subr.el index a5f8ead0a1b..1ed57765b12 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -2756,7 +2756,34 @@ Affects only hooks run in the current buffer." (let ((delay-mode-hooks t)) ,@body))) -;;; `when-let' and friends. +;;; `if-let*' and friends. +;;; +;;; We considered adding a `cond-let*' in late 2025: +;;; . +;;; We decided to add the `bind-and*' clause type to `cond*' instead. +;;; At first it seems simple to extend `if-let*'/`when-let*'/`and-let*' +;;; to `cond', but the extension is not unambiguous: there are multiple +;;; useful, incompatible ways to do it. +;;; In particular, it quickly becomes clear that one wants clauses that +;;; only establish bindings for proceeding clauses, instead of exiting +;;; the `cond-let*'. But then +;;; - Should these bindings be just like in `let*', or like in +;;; `if-let*'? In other words, should it be that if a binding +;;; evaluates to nil we skip the remaining bindings (bind them all to +;;; nil)? Both ways of doing it seem useful. +;;; - The parentheses quickly pile up. How can we avoid the programmer +;;; having to count parentheses? Some propose using square brackets +;;; (i.e., vectors) for the binding-only clauses, but Emacs Lisp is a +;;; traditional Lisp which uses exclusively parentheses for control +;;; constructs. Therefore, introducing square brackets here would be +;;; jarring to read. Another option would be to use symbols at the +;;; beginning of clauses, like `cond*' does. +;;; Whichever way one goes, the resulting macro ends up complicated, +;;; with a substantial learning burden. Adding `bind-and*' clauses to +;;; `cond*' gives us the desired functionality, and does not make +;;; `cond*' much more complicated. In other words, `cond*' is already +;;; complicated, and one complicated `cond'-extending macro is better +;;; than two. --spwhitton (defun internal--build-binding (binding prev-var) "Check and build a single BINDING with PREV-VAR." @@ -2803,7 +2830,7 @@ binding of SYMBOL is checked for nil, only. An older form for entries of VARLIST is also supported, where SYMBOL is omitted, i.e. (VALUEFORM). This means the same as (_ VALUEFORM). -This form is not recommended because many programmers find it +This form is not recommended because many Lisp programmers find it significantly less readable. A future release of Emacs may introduce a byte-compiler warning for uses of (VALUEFORM) in VARLIST." (declare (indent 2)