From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Michael Heerdegen Newsgroups: gmane.emacs.devel Subject: Re: Question on pcase Date: Fri, 23 Oct 2015 20:38:31 +0200 Message-ID: <87fv115t20.fsf@web.de> References: <871tcngdv2.fsf@gmail.com> <87k2qe1u09.fsf@web.de> <83r3kmrtat.fsf@gnu.org> <87r3kl22zk.fsf@web.de> <837fmdzpf2.fsf@gnu.org> <87oafp659p.fsf@web.de> <831tclzly9.fsf@gnu.org> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: ger.gmane.org 1445625556 882 80.91.229.3 (23 Oct 2015 18:39:16 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Fri, 23 Oct 2015 18:39:16 +0000 (UTC) Cc: Oleh Krehel , Emacs Development To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Fri Oct 23 20:39:04 2015 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1ZphEw-0007Og-Dt for ged-emacs-devel@m.gmane.org; Fri, 23 Oct 2015 20:38:58 +0200 Original-Received: from localhost ([::1]:41012 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZphEv-0001PW-Qp for ged-emacs-devel@m.gmane.org; Fri, 23 Oct 2015 14:38:57 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:38161) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZphEe-0001PP-05 for emacs-devel@gnu.org; Fri, 23 Oct 2015 14:38:40 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZphEa-0000x4-Qb for emacs-devel@gnu.org; Fri, 23 Oct 2015 14:38:39 -0400 Original-Received: from mout.web.de ([212.227.17.12]:51163) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZphEa-0000wa-Ez; Fri, 23 Oct 2015 14:38:36 -0400 Original-Received: from drachen.dragon ([90.186.0.245]) by smtp.web.de (mrweb102) with ESMTPSA (Nemesis) id 0LnS4I-1aRkTl0R94-00hdkV; Fri, 23 Oct 2015 20:38:35 +0200 In-Reply-To: <831tclzly9.fsf@gnu.org> (Eli Zaretskii's message of "Fri, 23 Oct 2015 17:41:34 +0300") User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.0.50 (gnu/linux) X-Provags-ID: V03:K0:8l3RNRsGj7ueyMMgcJj1vatJZF2PpLf888APShU1X3VejoGjn4/ LCXbQbqpEbwmiMGIfjJV0fxLlX9r5kL9gGphMhGPmAUcWRZuFWUFMp3cBdKuyaANjAxOaAm xD+p8Agrr83b0/eN5YafGbox4MNyWaM74uLS95iT9fQ3GPUykxHDyhYhpUcPbGIGApdnRlO Kn0fTYvU1qZEHNFAnDdhg== X-UI-Out-Filterresults: notjunk:1;V01:K0:7DVnB5REW/Y=:CS2oTFgoO/4Ag1hTb5ZKi4 UWwgwChaJvQ7OZrmT8CJbxU2K/L+kOx70bxHMmpQ1Wn5MVLvbvMZbuYDZnSNOCkvdeN/DsO0B cj1Qf1LSWN/OV7NjRHxsh8t08QYJi9n8UnWCsYccFlBEcIZniMoMZUVG+3Lh2+q4Cp78Wdkxw JPzJuKLsBQKrUKMExAk/OxSZmWnocK+znEHmaGeBmtZmbn9oMuPGiSzSbO469ErVtqBrTU/Ej MCCBMIv0jv4399W3sgDQSkizE2eODP/jNLlMgARXoZlQ94aubG/jLJQcMGDn6c34+uBF2BnZY /nFSca9GkjIa4jOyu4wSB+BQVsOJP5zwfeanhCvgBbp2CdV+uioUcvLFjEoiWtucaVLekhpv4 1+50Rxl45DlP3lVSFw5k/ZZh4dT5W/lCZO4BcQ+miQIesVEGyDl3lmdrs+arLQV1pZvg0evSc BWqvjC7FrDNuWOExs5LcMZl5jRVDffRavneDxkLj8Jzix+0lMQP/G3BLGF0SeLmWzPVB0YTHy 27vOJtGxCSZiAqosSKcF4ZE1bg8JCbwM9No6yf164UXkXa1TRgoQX1XrnIwZYPNbPp946SBt9 m0Gz5bizFzOzBSRlq/GmUG6QJ7GJNrCvSBelzNyftpvUGw7vPvQANvpcjOQyeL5hJM7MaFnJc 0kDObL+d3yTpFk8usA6c1IA1ENZTXvv9SM2qgRjFDcVZ+ivZGj6M4o3G0KYt86WiyBVAJjrck /nArRQfvn64rdpyQ9pLdjcs6cG/JnvQjMXzo3M0ZlP8fw+RsKg9GUKT+XyM= X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 212.227.17.12 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:192512 Archived-At: --=-=-= Content-Type: text/plain Eli Zaretskii writes: > Sounds good to me, thanks. I wrote a step by step introduction for people who (1) are frightened by `pcase' and (2) are willing to invest some (not much, but some) time to learn it. It is how I would describe `pcase' to a friend, maybe it is good for learning, maybe it is ugly, dunno. Some errors included. Someone who wants to try to learn pcase (Oleh? Oleh!) can help by reading it and telling me if it is understandable, and send corrections - or write a better introduction ;-) - and format it nicely for inclusion into Elpa or Emacs when it turns out to help people. This is an .el only because it was easier for me to format the code parts this way. --=-=-= Content-Type: application/emacs-lisp Content-Disposition: inline; filename=pcase-guide.el Content-Transfer-Encoding: quoted-printable ;; Understand `pcase' now! - A trivial guide to a powerful tool ;; =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D ;; From the docstring: ;; (pcase EXP &rest CASES) ;; Perform ML-style pattern matching on EXP. ;; What's "ML-style pattern matching"? If you know ML, pcase will ;; probably seem familiar to you. I don't; it isn't necessary to ;; understand `pcase'. ;; Any `pcase' form looks like this: (pcase EXPRESSION (PATTERN CODE ...) ...) ;; The EXPRESSION is first evaluated. Unlike the similar `cond', you ;; specify PATTERNs instead of CONDITIONs, and the PATTERNS are tested ;; for whether they match the evaluated EXPRESSION, in order. If one ;; PATTERN matches, it's CODE is evaluated. Like in `cond', all ;; branches are tested until one is found whose PATTERN matches. All ;; remaining branches are ignored. ;; ;; The PATTERNs are not lisp code, like `cl-loop' forms they are ;; written with an own simple language. There are a few simple basic ;; patterns, and you can combine them. ;; ;; A very useful thing is that matching a pattern can bind variables ;; as a side effect. This bindings are in effect when the CODE of ;; that branch is executed (actually, they are already in effect when ;; then the remaining parts of the pattern are processed; that fact ;; will prove useful later in this guide). ;; ;; All yet to understand is the semantic each of the basic PATTERNs. ;; Since you can do quite a lot with `pcase' - e.g. you can use it ;; like `let*', like `cond', like `case', like `destructuring-bind', ;; all in combination and more, it is clear that the semantic can't be ;; absolutely trivial. However, there are only very few basic forms ;; of patterns, and all are easy to understand. And those forms can ;; be combined. That's already it. I will explain the basic patterns ;; one after the other, you may want to take one or two breaks in ;; between. ;; ;; After you have read this, you will want to practise a bit with ;; self-written examples, or try to understand occurrances in the ;; sources, so that you get used to the different forms of patterns. ;; After that, you will never have to read this guide again, just ;; consult C-h f pcase RET and refresh your memories for 10 seconds or ;; so, that will suffice. ;; ;; So, let's look at the patterns! (BTW, you can define more patterns ;; with `pcase-defmacro', so C-h f pcase may list some more patterns ;; than described here.) Here are the built-in basic patterns: ;; ;; _ matches anything. ;; SYMBOL matches anything and binds it to SYMBOL. ;; (or PAT...) matches if any of the patterns matches. ;; (and PAT...) matches if all the patterns match. ;; 'VAL matches if the object is =E2=80=98equal=E2=80=99 to VAL. ;; ATOM is a shorthand for 'ATOM. ;; when ATOM is a keyword, an integer, or a string. ;; (pred FUN) matches if FUN applied to the object returns non-nil. ;; (guard BOOLEXP) matches if BOOLEXP evaluates to non-nil. ;; (let PAT EXP) matches if EXP matches PAT. ;; (app FUN PAT) matches if FUN applied to the object matches PAT. ;; ;; `QPAT Backquote-style pcase patterns. ;; That's just copied from the pcase docstring. The explanations are ;; already sufficient. Let's start with some examples for 'VAL and _ (pcase x ('a 1) ('"Hallo" 2) (_ 3)) ;; This will return 1 if x is bound to the symbol `a', 2 if x is bound ;; to a string equal to "Hallo", and 3 else. Hey, already two pattern ;; types learned! ;; ;; A SYMBOL (different from `_') "matches anything and binds it to ;; SYMBOL.". Example: (pcase x ('a 1) ('"Hallo" 2) (thing (message "%s is neither equal to 'a nor to \"Hallo\"." thing))) ;; If you use 'VAL, and VAL is a keyword, an integer, or a string, you ;; can leave out the quote (but only in these cases!) - in the above ;; example: (pcase x ('a 1) ("Hallo" 2) (thing (message "%s is neither equal to 'a nor to \"Hallo\"." thing))) ;; that's the case only for the string "Hallo" (why would it not be ;; useful to leave out the quote before the symbol `a'? ;; (or PAT...) matches if any of the patterns matches. Example: (pcase (car x) ((or 'a "Hallo") t) (_ nil)) ;; This will return t if the car of x references either the symbol `a' ;; or a string equal to "Hallo", and nil else. ;; ;; Now you've already learned 5 out of 11 basic types of patterns. ;; The remaining types are similarly easy. ;; (pred FUN) matches if FUN applied to the object returns non-nil. ;; "Object" just means the EXPRESSION that is matched. ;; ;; (and PAT...) matches if all the patterns match. (pcase x ((pred listp) 1) ((or (pred arrayp) (pred numberp)) 2) ((and (pred stringp) (pred (lambda (x) (string-match-p "blue" x)))) 3)) ;; This will return 1 if x is bound to a list, 2 if what x is bound to ;; fulfils eihter `arrayp' or `numberp'. And 3 if the binding of x is ;; a string that contains "blue". If none of that is true, no branch ;; matches, and `pcase' returns nil. ;; If matching any pattern established a variable binding, you can ;; refer to these bindings in the following patterns. ;; (guard BOOLEXP) matches if BOOLEXP evaluates to non-nil. (pcase thing ((and (pred stringp) string (guard (< (length string) 60))) (message "A short string: %S" string)) ((pred stringp) (message "A quite long string.")) (it (message "No string: %s" it))) ;; What's `it'? Oh, just a random SYMBOL name. ;; ;; A common pitfall is that you want to test a condition, but forget ;; to wrap it into `guard' because you used `cond' for the half of ;; your life. This will happen, so be warned. Always remember that ;; pcase is about "matching", though you _can_ test conditions as a ;; strange special case of matching via the `guard' pattern. ;; Exercise: We noted that `pcase' can be used to substitute `let*', ;; `cond' or `case' in your code. How would you roughly describe how ;; one can rewrite an arbitrary `let*', `cond' or `case' form using ;; `pcase'? (Hint: It's not hard.) ;; (let PAT EXP) matches if EXP matches PAT. ;; ;; Don't confuse this with the `let' special form. ;; ;; This is a pattern form that allows you to match a pattern PAT ;; against an _arbitrary_ expression EXP. This is not special, ;; matching PAT is done as you have learned, just against the EXP you ;; specify there, and not the EXPRESSION given to pcase at top level. ;; ;; Example: (pcase x ((and (pred numberp) (let (pred (lambda (x) (< 5 x))) (abs x))) t) (_ nil)) ;; This will return t if x is bound to a number whose absolute value ;; is larger than 5, like 7 or -13, and nil else, e.g. for 0 or -2 or ;; "Hallo". ;; Why is it called `let' then? Because you can use it to ;; conveniently bind variables. Example: (pcase string (and (pred stringp) (let l (length string)) (guard (> l 0))) l) ;; will return the string length of STRING if it is bound to a string ;; of non-zero length, and nil else. ;; BTW, the `pred' pattern form allows to simplify things like (pred (lambda (x) (< 5 x))) ;; Instead of (pred FUN) ;; there is the more general form (FUN ARG1 .. ARGn) ;; where FUN gets called with ARG1 .. ARGn as first n args and as ;; n+1'th argument the value being matched. ;; So we can write (pred < 5) instead of (pred (lambda (x) (< 5 x))) ;; For example (pcase x ((and (pred numberp) (let (and (pred < 5) abs-val) (abs x))) abs-val)) ;; will return the absolute value of x if x is a number and its ;; absolute value is larger than 5, and nil else. ;; (app FUN PAT) matches if FUN applied to the object matches PAT. ;; Example: (pcase x ((and (pred stringp) (app length (pred > 60))) t)) ;; will return t if and only if x is bound to a string with length ;; smaller than 60 chars. ;; Now, only one basic pattern form is missing: ;; ;; `QPAT Backquote-style pcase patterns. ;; ;; which allows for destructuring. ;; ;; Let's have a look at the description of this pattern form: ;; ;; -- `QPAT ;; ;; Backquote-style pcase patterns. ;; QPAT can take the following forms: ;; (QPAT1 . QPAT2) matches if QPAT1 matches the car and QPAT2 the c= dr. ;; [QPAT1 QPAT2..QPATn] matches a vector of length n and QPAT1..QPATn ma= tch ;; its 0..(n-1)th elements, respectively. ;; ,PAT matches if the pcase pattern PAT matches. ;; ATOM matches if the object is =E2=80=98equal=E2=80=99= to ATOM. ;; ATOM can be a symbol, an integer, or a string. ;; ;; Remember that, for example, (1 2 3) (1 . (2 3)) ;; desribe the same list structure using two different reader ;; syntaxes. When being read by the Lisp reader, those forms are ;; equal. So, in the above description, the =20=20 (QPAT1 . QPAT2) ;; form can be used to match any kind of arbitrary list, and you don't ;; need to use the read syntax using dot. ;; ;; If you are used to understand grammers: the above description of ;; `QPAT describes a quite simpel grammer. You make like to try it ;; out with some examples yourself, it's simple, though the results ;; might look unfamiliar. It doesn't take long to familiarize with ;; it, though. ;; ;; Note that though the backquote _syntax_ is used here, the backquote ;; macro is never called (!), just it's syntax is used to describe ;; patterns for destructuring because it is convenient to write, and ;; also a bit related to how backquote works. ;; Example: (pcase x (`("foo") t) (`("foo" ,a) a) (`("foo" . ,(and (pred listp) rest)) rest)) ;; This will return t if x is bound to a one element list with the ;; element equal to "foo". ;; ;; If x is bound to a list of two elements, and the first one is equal ;; to "foo", it returns the second element. ;; ;; If x is bound to a cons whose car equals "foo", and the cdr is ;; listp, this cdr is returned. Else, nil is returned. ;; ;; For destructuring, the `_' pattern is handy to match anything. ;; Pitfall: Be sure to unquote `_' if you don't want to match the ;; symbol `_' literally, because _ is not a QPAT. ;; ;; Example: The ;; pattern `(1 ,_ . ,_) ;; matches any list whoose first element is 1, and which has at least ;; one more element. ;; Just like with the "real" backquote, you can use "unquote" in lower ;; levels. Example: `(1 (x ,(and y (guard (< (+ x y) 10))))) ;; This pattern will match any list of two elements, where the first ;; element is 1, and the second argument is a list of two elements; ;; the first element is bound to the variable `x', and the second ;; element is bound to `y', and it is tested whether x + y is smaller ;; than 10. Only then the whole pattern matches. ;; Example: (defun swap-first-elts (thing) (pcase thing ((and (pred listp) `(,a ,b . ,rest)) `(,b ,a . ,rest)) (it it))) ;; This function will check whether the given argument is a list with ;; two or more elements. In this case, it returnes this list with the ;; first two elements swapped. In any other case, it just returns the ;; function argument. ;; ;; Here, again note that in the pattern, the backquote is part of the ;; pattern description: `(,a ,b . ,rest) ;; while in that branch's CODE: `(,b ,a . ,rest) ;; the backquote macro is indeed used. Though these are two ;; completely different things, the expressions look quite similar and ;; natural for the purpose, so you might understand now why backquote ;; and unquote were used to define destructuring pattern syntax. --=-=-= Content-Type: text/plain Thanks, Michael. --=-=-=--