1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-05 22:20:24 -08:00

Further document cond*'s bind-and*, document cond-let* discussion

* doc/lispref/control.texi (Conditionals): Pointer to cond*'s
bind-and* clauses for Lisp programmers looking for a cond-let*.
(cond* Macro): Document bind-and* clauses.
* lisp/subr.el: Comment summarizing recent discussion, for
future Emacs developers wondering about cond-let*.
This commit is contained in:
Sean Whitton 2025-11-05 16:07:56 +00:00
parent d3101630ab
commit ef1dfdc663
2 changed files with 54 additions and 3 deletions

View file

@ -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}

View file

@ -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:
;;; <https://lists.gnu.org/archive/html/emacs-devel/2025-09/msg00058.html>.
;;; 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."