From e824c914dabd92537a0d6e44eaa10bb4699c312f Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 3 Nov 2018 15:11:33 +0200 Subject: [PATCH] Improve documentation of destructuring-binding macros * lisp/emacs-lisp/pcase.el (pcase-dolist, pcase-let) (pcase-let*): Improve the doc strings. * doc/lispref/sequences.texi (Sequence Functions): Improve wording and rename arguments of seq-let to be more descriptive. Add a cross-reference to "Destructuring with pcase Patterns". * doc/lispref/control.texi (Pattern-Matching Conditional): Improve wording and the menu. (pcase Macro): Incorporate patch suggested by Paul Eggert . Reformat text. (Destructuring with pcase Patterns): Rename from "Destructuring patterns", and improve wording and indexing. --- doc/lispref/control.texi | 228 +++++++++++++++++++------------------ doc/lispref/sequences.texi | 20 ++-- lisp/emacs-lisp/pcase.el | 43 ++++--- 3 files changed, 160 insertions(+), 131 deletions(-) diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index 06c6622bf01..f80622e6025 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi @@ -419,65 +419,68 @@ This is not completely equivalent because it can evaluate @var{arg1} or @node Pattern-Matching Conditional @section Pattern-Matching Conditional @cindex pcase -@cindex pattern matching +@cindex pattern matching, programming style Aside from the four basic conditional forms, Emacs Lisp also has a pattern-matching conditional form, the @code{pcase} macro, a hybrid of @code{cond} and @code{cl-case} (@pxref{Conditionals,,,cl,Common Lisp Extensions}) that overcomes their limitations and introduces -the @dfn{pattern matching} programming style. -First, the limitations: +the @dfn{pattern matching programming style}. +The limitations that @code{pcase} overcomes are: @itemize -@item The @code{cond} form chooses among alternatives -by evaluating the predicate @var{condition} of each -of its clauses (@pxref{Conditionals}). -The primary limitation is that variables let-bound in @var{condition} -are not available to the clause's @var{body-forms}. +@item +The @code{cond} form chooses among alternatives by evaluating the +predicate @var{condition} of each of its clauses +(@pxref{Conditionals}). The primary limitation is that variables +let-bound in @var{condition} are not available to the clause's +@var{body-forms}. Another annoyance (more an inconvenience than a limitation) is that when a series of @var{condition} predicates implement -equality tests, there is a lot of repeated code. -For that, why not use @code{cl-case}? +equality tests, there is a lot of repeated code. (@code{cl-case} +solves this inconvenience.) @item The @code{cl-case} macro chooses among alternatives by evaluating the equality of its first argument against a set of specific values. -The limitations are two-fold: + +Its limitations are two-fold: @enumerate -@item The equality tests use @code{eql}. -@item The values must be known and written in advance. +@item +The equality tests use @code{eql}. +@item +The values must be known and written in advance. @end enumerate @noindent These render @code{cl-case} unsuitable for strings or compound -data structures (e.g., lists or vectors). -For that, why not use @code{cond}? -(And here we end up in a circle.) +data structures (e.g., lists or vectors). (@code{cond} doesn't have +these limitations, but it has others, see above.) @end itemize @noindent Conceptually, the @code{pcase} macro borrows the first-arg focus of @code{cl-case} and the clause-processing flow of @code{cond}, replacing @var{condition} with a generalization of -the equality test called @dfn{matching}, +the equality test which is a variant of @dfn{pattern matching}, and adding facilities so that you can concisely express a clause's predicate, and arrange to share let-bindings between a clause's predicate and @var{body-forms}. The concise expression of a predicate is known as a @dfn{pattern}. -When the predicate, called on the value of the first arg, -returns non-@code{nil}, the pattern matches the value -(or sometimes ``the value matches the pattern''). +When the predicate, called on the value of the first arg, returns +non-@code{nil}, we say that ``the pattern matches the value'' (or +sometimes ``the value matches the pattern''). @menu -* The @code{pcase} macro: pcase Macro. Plus examples and caveats. +* The @code{pcase} macro: pcase Macro. Includes examples and caveats. * Extending @code{pcase}: Extending pcase. Define new kinds of patterns. -* Backquote-Style Patterns: Backquote Patterns. Structural matching. -* Destructuring patterns:: Using pcase patterns to extract subfields. +* Backquote-Style Patterns: Backquote Patterns. Structural patterns matching. +* Destructuring with pcase Patterns:: Using pcase patterns to extract subfields. @end menu @node pcase Macro @@ -498,30 +501,30 @@ of the last of @var{body-forms} in the successful clause. Otherwise, @code{pcase} evaluates to @code{nil}. @end defmac -Each @var{pattern} has to be a @dfn{pcase pattern}, which can either -use one of the core patterns defined below, or use one of the patterns -defined via @code{pcase-defmacro}. +@cindex pcase pattern +Each @var{pattern} has to be a @dfn{pcase pattern}, which can use +either one of the core patterns defined below, or one of the patterns +defined via @code{pcase-defmacro} (@pxref{Extending pcase}). -The rest of this subsection -describes different forms of core patterns, -presents some examples, -and concludes with important caveats on using the -let-binding facility provided by some pattern forms. -A core pattern can have the following forms: +The rest of this subsection describes different forms of core +patterns, presents some examples, and concludes with important caveats +on using the let-binding facility provided by some pattern forms. A +core pattern can have the following forms: @table @code @item _ Matches any @var{expval}. -This is known as @dfn{don't care} or @dfn{wildcard}. +This is also known as @dfn{don't care} or @dfn{wildcard}. @item '@var{val} -Matches if @var{expval} is @code{equal} to @var{val}. +Matches if @var{expval} is equals @var{val}. The comparison is done +as if by @code{equal} (@pxref{Equality Predicates}). @item @var{keyword} @itemx @var{integer} @itemx @var{string} -Matches if @var{expval} is @code{equal} to the literal object. +Matches if @var{expval} equals the literal object. This is a special case of @code{'@var{val}}, above, possible because literal objects of these types are self-quoting. @@ -533,17 +536,17 @@ Matches any @var{expval}, and additionally let-binds @var{symbol} to If @var{symbol} is part of a sequencing pattern @var{seqpat} (e.g., by using @code{and}, below), the binding is also available to the portion of @var{seqpat} following the appearance of @var{symbol}. -This usage has some caveats (@pxref{pcase-symbol-caveats,,caveats}). +This usage has some caveats, see @ref{pcase-symbol-caveats,,caveats}. Two symbols to avoid are @code{t}, which behaves like @code{_} -(above) and is deprecated, and @code{nil}, which signals error. +(above) and is deprecated, and @code{nil}, which signals an error. Likewise, it makes no sense to bind keyword symbols (@pxref{Constant Variables}). @item (pred @var{function}) Matches if the predicate @var{function} returns non-@code{nil} when called on @var{expval}. -@var{function} can have one of the possible forms: +the predicate @var{function} can have one of the following forms: @table @asis @item function name (a symbol) @@ -570,20 +573,17 @@ the actual function call becomes: @w{@code{(= 42 @var{expval})}}. @item (app @var{function} @var{pattern}) Matches if @var{function} called on @var{expval} returns a value that matches @var{pattern}. -@var{function} can take one of the -forms described for @code{pred}, above. -Unlike @code{pred}, however, -@code{app} tests the result against @var{pattern}, -rather than against a boolean truth value. +@var{function} can take one of the forms described for @code{pred}, +above. Unlike @code{pred}, however, @code{app} tests the result +against @var{pattern}, rather than against a boolean truth value. @item (guard @var{boolean-expression}) Matches if @var{boolean-expression} evaluates to non-@code{nil}. @item (let @var{pattern} @var{expr}) -Evaluates @var{expr} to get @var{exprval} -and matches if @var{exprval} matches @var{pattern}. -(It is called @code{let} because -@var{pattern} can bind symbols to values using @var{symbol}.) +Evaluates @var{expr} to get @var{exprval} and matches if @var{exprval} +matches @var{pattern}. (It is called @code{let} because @var{pattern} +can bind symbols to values using @var{symbol}.) @end table @cindex sequencing pattern @@ -596,18 +596,16 @@ but instead of processing values, they process sub-patterns. @table @code @item (and @var{pattern1}@dots{}) -Attempts to match @var{pattern1}@dots{}, in order, -until one of them fails to match. -In that case, @code{and} likewise fails to match, -and the rest of the sub-patterns are not tested. -If all sub-patterns match, @code{and} matches. +Attempts to match @var{pattern1}@dots{}, in order, until one of them +fails to match. In that case, @code{and} likewise fails to match, and +the rest of the sub-patterns are not tested. If all sub-patterns +match, @code{and} matches. @item (or @var{pattern1} @var{pattern2}@dots{}) Attempts to match @var{pattern1}, @var{pattern2}, @dots{}, in order, -until one of them succeeds. -In that case, @code{or} likewise matches, -and the rest of the sub-patterns are not tested. -(Note that there must be at least two sub-patterns. +until one of them succeeds. In that case, @code{or} likewise matches, +and the rest of the sub-patterns are not tested. (Note that there +must be at least two sub-patterns. Simply @w{@code{(or @var{pattern1})}} signals error.) @c Issue: Is this correct and intended? @c Are there exceptions, qualifications? @@ -1042,12 +1040,11 @@ Both use a single backquote construct (@pxref{Backquote}). This subsection describes @dfn{backquote-style patterns}, a set of builtin patterns that eases structural matching. -For background, @xref{Pattern-Matching Conditional}. +For background, @pxref{Pattern-Matching Conditional}. -@dfn{Backquote-style patterns} are a powerful set of -@code{pcase} pattern extensions (created using @code{pcase-defmacro}) -that make it easy to match @var{expval} against -specifications of its @emph{structure}. +Backquote-style patterns are a powerful set of @code{pcase} pattern +extensions (created using @code{pcase-defmacro}) that make it easy to +match @var{expval} against specifications of its @emph{structure}. For example, to match @var{expval} that must be a list of two elements whose first element is a specific string and the second @@ -1173,87 +1170,102 @@ evaluation results: (evaluate '(sub 1 2) nil) @result{} error @end example -@node Destructuring patterns -@subsection Destructuring Patterns -@cindex destructuring patterns +@node Destructuring with pcase Patterns +@subsection Destructuring with @code{pcase} Patterns +@cindex destructuring with pcase patterns Pcase patterns not only express a condition on the form of the objects -they can match but they can also extract sub-fields of those objects. -Say we have a list and want to extract 2 elements from it with the -following code: +they can match, but they can also extract sub-fields of those objects. +For example we can extract 2 elements from a list that is the value of +the variable @code{my-list} with the following code: @example - (pcase l + (pcase my-list (`(add ,x ,y) (message "Contains %S and %S" x y))) @end example This will not only extract @code{x} and @code{y} but will additionally -test that @code{l} is a list containing exactly 3 elements and whose -first element is the symbol @code{add}. If any of those tests fail, -@code{pcase} will directly return @code{nil} without calling +test that @code{my-list} is a list containing exactly 3 elements and +whose first element is the symbol @code{add}. If any of those tests +fail, @code{pcase} will immediately return @code{nil} without calling @code{message}. -@dfn{Destructuring} of an object is an operation that extracts -multiple values stored in the object, e.g., the 2nd and the 3rd -element of a list or a vector. @dfn{Destructuring binding} is -similar to a local binding (@pxref{Local Variables}), but it gives -values to multiple elements of a variable by extracting those values -from an object of compatible structure. +Extraction of multiple values stored in an object is known as +@dfn{destructuring}. Using @code{pcase} patterns allows to perform +@dfn{destructuring binding}, which is similar to a local binding +(@pxref{Local Variables}), but gives values to multiple elements of +a variable by extracting those values from an object of compatible +structure. -The macros described in this section use @dfn{destructuring -patterns}, which are normal Pcase patterns used in a context where we -presume that the object does match the pattern, and we only want -to extract some subfields. For example: +The macros described in this section use @code{pcase} patterns to +perform destructuring binding. The condition of the object to be of +compatible structure means that the object must match the pattern, +because only then the object's subfields can be extracted. For +example: @example - (pcase-let ((`(add ,x ,y) l)) + (pcase-let ((`(add ,x ,y) my-list)) (message "Contains %S and %S" x y)) @end example @noindent does the same as the previous example, except that it directly tries -to extract @code{x} and @code{y} from @code{l} without first verifying -if @code{l} is a list which has the right number of elements and has -@code{add} as its first element. -The precise behavior when the object does not actually match the -pattern is undefined, although the body will not be silently skipped: -either an error is signaled or the body is run with some of the -variables potentially bound to arbitrary values like @code{nil}. +to extract @code{x} and @code{y} from @code{my-list} without first +verifying if @code{my-list} is a list which has the right number of +elements and has @code{add} as its first element. The precise +behavior when the object does not actually match the pattern is +undefined, although the body will not be silently skipped: either an +error is signaled or the body is run with some of the variables +potentially bound to arbitrary values like @code{nil}. + +The pcase patterns that are useful for destructuring bindings are +generally those described in @ref{Backquote Patterns}, since they +express a specification of the structure of objects that will match. + +For an alternative facility for destructuring binding, see +@ref{seq-let}. @defmac pcase-let bindings body@dots{} -Bind variables according to @var{bindings} and then eval @var{body}. +Perform desctructuring binding of variables according to +@var{bindings}, and then evaluate @var{body}. @var{bindings} is a list of bindings of the form @w{@code{(@var{pattern} @var{exp})}}, where @var{exp} is an expression to evaluate and -@var{pattern} is a destructuring pattern. +@var{pattern} is a @code{pcase} pattern. -All @var{exp}s are evaluated first after which they are matched +All @var{exp}s are evaluated first, after which they are matched against their respective @var{pattern}, introducing new variable -bindings which can then be used inside @var{body}. +bindings that can then be used inside @var{body}. The variable +bindings are produced by destructuring binding of elements of +@var{pattern} to the values of the corresponding elements of the +evaluated @var{exp}. @end defmac @defmac pcase-let* bindings body@dots{} -Bind variables according to @var{bindings} and then eval @var{body}. +Perform desctructuring binding of variables according to +@var{bindings}, and then evaluate @var{body}. @var{bindings} is a list of bindings of the form @code{(@var{pattern} @var{exp})}, where @var{exp} is an expression to evaluate and -@var{pattern} is a destructuring pattern. - -Unlike @code{pcase-let}, but like @code{let*}, each @var{exp} is -matched against its corresponding @var{pattern} before passing to the -next element of @var{bindings}, so the variables introduced in each -binding are available in the @var{exp}s that follow it, additionally -to being available in @var{body}. +@var{pattern} is a @code{pcase} pattern. The variable bindings are +produced by destructuring binding of elements of @var{pattern} to the +values of the corresponding elements of the evaluated @var{exp}. + +Unlike @code{pcase-let}, but similarly to @code{let*}, each @var{exp} +is matched against its corresponding @var{pattern} before processing +the next element of @var{bindings}, so the variable bindings +introduced in each one of the @var{bindings} are available in the +@var{exp}s of the @var{bindings} that follow it, additionally to +being available in @var{body}. @end defmac -@findex dolist @defmac pcase-dolist (pattern list) body@dots{} -This construct executes @var{body} once for each element of -@var{list}, in a context where the variables appearing in the the -destructuring pattern @var{pattern} are bound to the corresponding -values found in the element. -When @var{pattern} is a simple variable, this ends up being equivalent -to @code{dolist}. +Execute @var{body} once for each element of @var{list}, on each +iteration performing a destructuring binding of variables in +@var{pattern} to the values of the corresponding subfields of the +element of @var{list}. The bindings are performed as if by +@code{pcase-let}. When @var{pattern} is a simple variable, this ends +up being equivalent to @code{dolist} (@pxref{Iteration}). @end defmac diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index 51d724cb1d8..60d017c3e44 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -1049,15 +1049,18 @@ that @var{sequence} can be a list, vector or string. This is primarily useful for side-effects. @end defmac -@defmac seq-let arguments sequence body@dots{} +@anchor{seq-let} +@defmac seq-let var-sequence val-sequence body@dots{} @cindex sequence destructuring - This macro binds the variables defined in @var{arguments} to the -elements of @var{sequence}. @var{arguments} can themselves include -sequences, allowing for nested destructuring. + This macro binds the variables defined in @var{var-sequence} to the +values that are the corresponding elements of @var{val-sequence}. +This is known as @dfn{destructuring binding}. The elements of +@var{var-sequence} can themselves include sequences, allowing for +nested destructuring. -The @var{arguments} sequence can also include the @code{&rest} marker -followed by a variable name to be bound to the rest of -@code{sequence}. +The @var{var-sequence} sequence can also include the @code{&rest} +marker followed by a variable name to be bound to the rest of +@var{val-sequence}. @example @group @@ -1081,6 +1084,9 @@ followed by a variable name to be bound to the rest of @end group @result{} [3 4] @end example + +The @code{pcase} patterns provide an alternative facility for +destructuring binding, see @ref{Destructuring with pcase Patterns}. @end defmac @defun seq-random-elt sequence diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el index 2e89ae0779a..fde3bdb27f3 100644 --- a/lisp/emacs-lisp/pcase.el +++ b/lisp/emacs-lisp/pcase.el @@ -264,10 +264,14 @@ variable name being but a special case of it)." ;;;###autoload (defmacro pcase-let* (bindings &rest body) - "Like `let*' but where you can use `pcase' patterns for bindings. -BODY should be an expression, and BINDINGS should be a list of bindings -of the form (PATTERN EXP). -See `pcase-let' for discussion of how PATTERN is matched." + "Like `let*', but supports destructuring BINDINGS using `pcase' patterns. +As with `pcase-let', BINDINGS are of the form (PATTERN EXP), but the +EXP in each binding in BINDINGS can use the results of the destructuring +bindings that precede it in BINDINGS' order. + +Each EXP should match (i.e. be of compatible structure) to its +respective PATTERN; a mismatch may signal an error or may go +undetected, binding variables to arbitrary values, such as nil." (declare (indent 1) (debug ((&rest (pcase-PAT &optional form)) body))) (let ((cached (gethash bindings pcase--memoize))) @@ -280,13 +284,16 @@ See `pcase-let' for discussion of how PATTERN is matched." ;;;###autoload (defmacro pcase-let (bindings &rest body) - "Like `let' but where you can use `pcase' patterns for bindings. -BODY should be a list of expressions, and BINDINGS should be a list of bindings -of the form (PATTERN EXP). -The PATTERNs are only used to extract data, so the code does not test -whether the data does match the corresponding patterns: a mismatch -may signal an error or may go undetected, binding variables to arbitrary -values, such as nil." + "Like `let', but supports destructuring BINDINGS using `pcase' patterns. +BODY should be a list of expressions, and BINDINGS should be a list of +bindings of the form (PATTERN EXP). +All EXPs are evaluated first, and then used to perform destructuring +bindings by matching each EXP against its respective PATTERN. Then +BODY is evaluated with those bindings in effect. + +Each EXP should match (i.e. be of compatible structure) to its +respective PATTERN; a mismatch may signal an error or may go +undetected, binding variables to arbitrary values, such as nil." (declare (indent 1) (debug pcase-let*)) (if (null (cdr bindings)) `(pcase-let* ,bindings ,@body) @@ -304,11 +311,15 @@ values, such as nil." ;;;###autoload (defmacro pcase-dolist (spec &rest body) - "Superset of `dolist' where the VAR binding can be a `pcase' PATTERN. -More specifically, this is just a shorthand for the following combination -of `dolist' and `pcase-let': - - (dolist (x LIST) (pcase-let ((PATTERN x)) BODY...)) + "Eval BODY once for each set of bindings defined by PATTERN and LIST elements. +PATTERN should be a `pcase' pattern describing the structure of +LIST elements, and LIST is a list of objects that match PATTERN, +i.e. have a structure that is compatible with PATTERN. +For each element of LIST, this macro binds the variables in +PATTERN to the corresponding subfields of the LIST element, and +then evaluates BODY with these bindings in effect. The +destructuring bindings of variables in PATTERN to the subfields +of the elements of LIST is performed as if by `pcase-let'. \n(fn (PATTERN LIST) BODY...)" (declare (indent 1) (debug ((pcase-PAT form) body))) (if (pcase--trivial-upat-p (car spec)) -- 2.39.2