From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: "Garreau\, Alexandre" Newsgroups: gmane.emacs.devel Subject: pcase-lambda usage [Was: Re: Replace trivial pcase occurrences in the Emacs sources] Date: Mon, 29 Oct 2018 19:49:00 +0100 Message-ID: <874ld4twpv.fsf_-_@portable.galex-713.eu> References: <20151216202605.GA3752@acm.fritz.box> <871t9yk98g.fsf@fencepost.gnu.org> <568936F0.3060505@yandex.ru> <87wprqitj5.fsf@fencepost.gnu.org> <56893C8C.3060200@yandex.ru> <87oad2irtd.fsf@fencepost.gnu.org> <5689456A.1010601@yandex.ru> <87egdy8tyz.fsf@fencepost.gnu.org> <56895FDE.4060406@yandex.ru> <8760za8r4a.fsf@fencepost.gnu.org> <87h9iunkcg.fsf@web.de> <87h8hc4xw2.fsf_-_@web.de> <83tvlcsnee.fsf@gnu.org> <86mur137n8.fsf@gmail.com> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Trace: blaine.gmane.org 1540852623 22557 195.159.176.226 (29 Oct 2018 22:37:03 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Mon, 29 Oct 2018 22:37:03 +0000 (UTC) User-Agent: Gnus (5.13), GNU Emacs 25.1.1 (i686-pc-linux-gnu, GTK+ Version 3.22.11) of 2017-09-15, modified by Debian Cc: emacs-devel@gnu.org To: Andy Moreton Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Mon Oct 29 23:36:59 2018 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1gHG9Z-0005iH-VZ for ged-emacs-devel@m.gmane.org; Mon, 29 Oct 2018 23:36:58 +0100 Original-Received: from localhost ([::1]:49386 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gHGBg-0004jB-G9 for ged-emacs-devel@m.gmane.org; Mon, 29 Oct 2018 18:39:08 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:37404) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gHGBV-0004ha-5w for emacs-devel@gnu.org; Mon, 29 Oct 2018 18:38:58 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gHGBT-0001VS-Re for emacs-devel@gnu.org; Mon, 29 Oct 2018 18:38:57 -0400 Original-Received: from portable.galex-713.eu ([2a00:5884:8305::1]:59522) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1gHGBT-0001Sx-EG for emacs-devel@gnu.org; Mon, 29 Oct 2018 18:38:55 -0400 Original-Received: from [::1] (helo=portable.galex-713.eu) by portable.galex-713.eu with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89) (envelope-from ) id 1gHCay-0005dx-9L; Mon, 29 Oct 2018 19:49:01 +0100 In-Reply-To: (Andy Moreton's message of "Mon, 29 Oct 2018 14:47:02 +0000") PGP-FINGERPRINT: E109 9988 4197 D7CB B0BC 5C23 8DEB 24BA 867D 3F7F Accept-Language: fr, en, eo, it, br X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:5884:8305::1 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 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" Xref: news.gmane.org gmane.emacs.devel:230780 Archived-At: >From what I learnt from pcase-lambda (I wished there were a simple `match' implemented over `cl-destructuring-bind', similar to one-matching cl implementations, on which would be based pcase (which match several alternatively), and pcase-*), here a docstring that seems better to me, probably not ideal, if anybody can improve, tell it: #+BEGIN_SRC elisp "Like `lambda' but destructure each PATTERN with `pcase'. It is not self-quoting: the result of `pcase-lambda' is a lambda expression like returned by `lambda'. Only then, the lambda expression may be stored as a function value of a symbol with `fset', passed to `funcall' or `mapcar', etc. The first argument, of the form (PATTERN...) also accepts the usual &optional and &rest keywords accepted in lambda expressions arglist, but every formal element can be any pattern accepted by `pcase' (a mere variable name being but a special case of it). PATTERN should take the same form as a `pcase' pattern. A given subpattern inside PATTERN might not match values (equality) or even types, except for each of its subsequences, which *have* to exist, have the right type, and be in the right place. DOCSTRING is an optional documentation string just as in `lambda'. Note that each PATTERN that is a special `pcase' pattern will get an automatically assigned name: you may want to put \\(fn YOUR ARGLIST (AS YOU INTENDED)) so to reflect your patterns. INTERACTIVE, should as well be a call to the function `interactive'. It may be omitted. BODY should be the usual list of Lisp expressions `lambda' takes as lasts arguments. \(fn (PATTERN...) [DOCSTRING] [INTERACTIVE] BODY)" #+END_SRC As I never saw pcase-lambda before and this was the first time I see anything about it, here a sample of the process I went trough: C-h f pcase-lambda RET =3D> (pcase-lambda LAMBDA-LIST &rest BODY) Okay, so there=E2=80=99s only one pattern, so it=E2=80=99s not pattern matc= hing (as its name might wrongly suggest), like in OCaml =E2=80=9Cfunction=E2=80=9D opera= tor, it=E2=80=99s simple destructuring, exactely like `cl-destructuring-bind', but richer. Also: no =E2=80=9Cdocstring=E2=80=9D? no =E2=80=9Cinteractive=E2=80=9D? rea= lly? doesn=E2=80=99t `pcase-lambda' happen to work by translating to a lambda? does it override docstrings (that might make sense, so to replace the arglist in help by the pcase pattern) and interactiveness (that doesn=E2=80=99t make sense at all: so we can=E2=80=99t define after pcase-lambda a pcase-defun and pcase-defmacro (w= ait, this one is already occupied=E2=80=A6 it should have been a different name = then: this has gone too far)?)? let=E2=80=99s try: #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C-c c c c") (pcase-lambda (a `(,b ,c)) "test" (interactive) (insert ?2))) #+END_SRC Then =E2=80=9CC-c c c c=E2=80=9D, actually inserts 2. C-h k C-c c c c =3D> the docstring is =E2=80=9Ctest=E2=80=9D. So these must be implicitly considered as inside =E2=80=9Cbody=E2=80=9D=E2= =80=A6 that doesn=E2=80=99t make really sense: even `lambda' explictly tells about them, while not recommanding against not using docstring in anonymous function. Docstring: > Like =E2=80=98lambda=E2=80=99 but allow each argument to be a pattern. > I.e. accepts the usual &optional and &rest keywords, but every > formal argument can be any pattern accepted by =E2=80=98pcase=E2=80=99 (a= mere > variable name being but a special case of it). Oh, does that mean I can, like cl-destructuring-bind, put &optional and &rest wherever I want in any subpattern? So then pcase is strictly a superset of cl-destructuring-bind: how sad one is not implemented upon the other, then (cl-destructuring-bind ought to have a no-error function so that to be usable to build something upon it). #+BEGIN_SRC emacs-lisp (pcase-lambda (a (b c)) c) ;; =3D> pcase--macroexpand: Unknown a pattern: (a (b c)) =20=20 (pcase (list 1 (list 2 3)) ((a (b c)) c)) ;; =3D> pcase--macroexpand: Unknown a pattern: (a (b c)) =20=20 ;; Oh, I recall, pcase behaves differently than normal ;; pattern-matching, I must quote everything! =20=20 (pcase-lambda '(a (b c)) c) ;; =3D> pcase--macroexpand: Unknown a pattern: (a (b c)) =20=20 ;; Uh, isn=E2=80=99t ' a pattern in pcase? It errs out just the same! =20=20 (pcase '(1 (2 3)) ('(a (b c)) t)) ; =3D> nil ; But=E2=80=A6 why aren=E2= =80=99t they ; behaving the same? Why ; don=E2=80=99t I get an error? =20=20 ;; Oh I recall! I must use =E2=80=9C`=E2=80=9D: (pcase '(1 (2 3)) (`(,a (,b ,c)) t)) ; =3D> t (It Works!=E2=84=A2) =20=20 So: (pcase-lambda `(,a (,b ,c)) c) ; =3D> Wrong type argument: symbolp, (\, a) ;; Uh, isn=E2=80=99t that the correct way to use pcase? =20=20 ;; Let=E2=80=99s go back before, when it unexpectedly didn=E2=80=99t err,= wasn=E2=80=99t I ;; right? (pcase '(a (b c)) ('(a (b c)) t)) ; =3D> t (so indeed, I was right) =20=20 (pcase 'a ('a t)) ; =3D> t (indeed, when you quote you compare symbols, ; not values (nor bind anything)) =20=20 ;; So let=E2=80=99s try the simplest case: (pcase-lambda 'a a) ; =3D> (lambda (quote a) a) =20=20 ;; Oh, if I=E2=80=99m right, that =E2=80=9Cquote=E2=80=9D isn=E2=80=99t a= quotation, but an argument =20=20 ((lambda (quote a) a) 1 2) ; =3D> 2 (it seems so) =20=20 ((lambda (quote a) a) 'a) ;; =3D> Wrong number of arguments: (lambda (quote a) a), 1 ;; it *is* so =20=20 ;; Then, if I=E2=80=99m right, the correct usage is: =20=20 (pcase-lambda (a) a) ; =3D> (lambda (a) a) ; okay it returns clean ; lambdas if possible =20=20 (pcase-lambda (a (2 3)) a) ;; =3D> (lambda (a arg0) (let nil a)) ;; ah, I was wrong, nevermind (why this useless `let nil'?). Btw, may I ;; suggest an argument name such as `list' instead of `arg0' that would ;; indicate type, as it is traditional in lisp (and only number list0, ;; list1, etc. if there are several of them) =20=20 ((pcase-lambda (a '(2 3)) a) 1 (list 2 3)) ;; =3D> Invalid function: (pcase-lambda (a (quote (2 3)) a)) =20=20 ;; oh, there must be a special elisp-related reason why even if it does ;; return a lambda it can=E2=80=99t be used right away (maybe because it= =E2=80=99s a ;; lisp-2?). Since `lambda' doc says it=E2=80=99s =E2=80=9Cself-quoting= =E2=80=9D and =E2=80=9Cmay be ;; used as a function=E2=80=9D, and that=E2=80=99s not true the same way = here, it should ;; be said. =20=20 (funcall (pcase-lambda (a '(2 3)) a) 1 (list 2 3)) =3D> 1 =20=20 ;; Okay so since `lambda' docstring takes the time to say it works with ;; `funcall' and `mapcar', then probably `pcase-lambda' should too, so ;; an example of how to properly use it comes immediatly to mind. =20=20 (funcall (pcase-lambda (a '(2 3)) a) 1 (list 1 1)) =20=20 ;; Okay it=E2=80=99s not normal, it should give an error, or returns nil,= I think. (funcall (pcase-lambda (a '(b c)) c) 1 '(2 3)) ; let=E2=80=99s see what= =E2=80=99s wrong ;; =3D> Symbol=E2=80=99s value as variable is void: c ;; c isn=E2=80=99t bound? =20=20 ;; Maybe quoting, again? (funcall (pcase-lambda (a `(,b ,c)) c) 1 '(2 3)) ; =3D> 3 (yes, quoting) =20=20 ;; Let=E2=80=99s try again not to match: (funcall (pcase-lambda (`(1 ,b 3)) a) 2) ;; =3D> let*: Wrong type argument: listp, 2 ;; Cool! *Now* it gives errors! (funcall (pcase-lambda (`(1 ,b 3)) b) '(2)) ;; =3D> nil (okay=E2=80=A6 I don=E2=80=99t understand) (funcall (pcase-lambda (`(1 ,b 3)) b) '(1 2 4)) ;; =3D> 2 (mmmh=E2=80=A6 seems lazy on arity and eq-uality=E2=80=A6 but p= icky on types) (mapcar (pcase-lambda (`(1 (2 ,c 4 . 5))) c) '((1 (2 3 4 . 5)) (1 (2)) (1 (2 3 4 5 6 . 7)))) ;; =3D> (2 nil) (indeed, it doesn=E2=80=99t give a fuck about arity) (funcall (pcase-lambda (`(1 (2 ,c 4 . 5))) c) '(1 (2 . 3))) ;; =3D> let*: Wrong type argument: listp, 3 ;; But it want sequences to be the right type =20=20 (funcall (pcase-lambda (`(1 (2 ,c 4 . 5))) c) '(1 (2 3 . 4))) ;; =3D> let*: Wrong type argument: listp, 4 ;; Even when it doesn=E2=80=99t need to, apparently, so it=E2=80=99s not = even laziness (funcall (pcase-lambda (`(1 (2 ,c (4 5)))) c) '(1 (2 3))) ; =3D> 3 (funcall (pcase-lambda (`(1 (2 ,c (4 5)))) c) '(1 (2 3 [4 5]))) ;; =3D> let*: Wrong type argument: listp, [4 5] ;; Yeah, sequences types, only that (it should have used =E2=80=9Celt=E2= =80=9D, so it ;; would have always worked (you then only need to have the same ;; sequences tree structure) =20=20 ;; let=E2=80=99s try out how that acts on arglist features (funcall (pcase-lambda (a `(&rest ,rest)) rest) 1 2 3) ;; =3D> Wrong number of arguments: (lambda (a arg0) (let* ((x (car ;; arg0)) (x (cdr arg0)) (x (car x)) (x (cdr x))) (let ((rest x)) ;; rest))), 3 =20=20 ;; Ah indeed, to do that it would be: (funcall (pcase-lambda (a &rest rest) rest) 1 2 3) ; =3D> (2 3) ;; yes &rest works =20=20 ;; So, I want, with correct args: (funcall (pcase-lambda (a `(&rest ,rest)) rest) 1 '(2 3)) ; =3D> 3 ;; UH??? =20=20 (funcall (pcase-lambda (a `(,b ,c &rest ,rest)) rest) 1 '(2 3 4 5 6 7)) ;; =3D> 5 (okay something strange is going) =20=20 (funcall (pcase-lambda (a `(,b ,c &optional ,opt)) opt) 1 '(2 3 4)) (funcall (pcase-lambda (a `(,b ,c &optional ,opt)) opt) 1 '(2 3)) ;; =3D> nil =3D> nil (what?) =20=20 (funcall (pcase-lambda (a `(,b ,c &optional ,opt)) opt) 1 '(2 3 4 5)) ;; =3D> 5 (there=E2=80=99s one argument too much) =20=20 ;; Don=E2=80=99t say me &optional is matched as an argument? (funcall (pcase-lambda (a `(,b ,c &optional ,opt)) (list b c opt)) 1 '(2 3 4 5)) ; =3D> (2 3 5) ;; Is seems to >< so in the end it doesn=E2=80=99t work? =20=20 (funcall (pcase-lambda (a &optional ,opt) (cons a opt)) 1 '(2 3 4 5)) ;; =3D> pcase--macroexpand: Unknown , pattern: (\, opt) =20=20 (funcall (pcase-lambda (a &optional opt) (cons a opt)) 1 2) ; =3D> (1 . 2) (funcall (pcase-lambda (a &optional opt) (cons a opt)) 1) ; =3D> (1) =20=20 ;; Isn=E2=80=99t behavior not supposed to change depending on nesting? =20=20 (apply (pcase-lambda (a (b (c))) (cons a c)) '(a (b (c)))) ;; =3D> pcase--macroexpand: Unknown b pattern: (b (c)) =20=20 ;; It seems it becomes real `pcase' only from second level: (apply (pcase-lambda (a `(,b (,c))) (cons a c)) '(a (b (c)))) ;; =3D> (a . c) =20=20 ;; So first level is what normal lambda (or destructuring-bind) does, ;; that looks like real pattern matching that look like the matched ;; data, like in ocaml, haskell, etc. and second level is pcase syntax=E2= =80=A6 #+END_SRC