[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
pcase-lambda usage [Was: Re: Replace trivial pcase occurrences in the Em
From: |
Garreau\, Alexandre |
Subject: |
pcase-lambda usage [Was: Re: Replace trivial pcase occurrences in the Emacs sources] |
Date: |
Mon, 29 Oct 2018 19:49:00 +0100 |
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 |
>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 => (pcase-lambda LAMBDA-LIST &rest BODY)
Okay, so there’s only one pattern, so it’s not pattern matching (as its
name might wrongly suggest), like in OCaml “function” operator, it’s
simple destructuring, exactely like `cl-destructuring-bind', but richer.
Also: no “docstring”? no “interactive”? really? doesn’t `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’t make sense at all: so we
can’t define after pcase-lambda a pcase-defun and pcase-defmacro (wait,
this one is already occupied… it should have been a different name then:
this has gone too far)?)?
let’s 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 “C-c c c c”, actually inserts 2.
C-h k C-c c c c => the docstring is “test”.
So these must be implicitly considered as inside “body”… that doesn’t
make really sense: even `lambda' explictly tells about them, while not
recommanding against not using docstring in anonymous function.
Docstring:
> Like ‘lambda’ 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 ‘pcase’ (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)
;; => pcase--macroexpand: Unknown a pattern: (a (b c))
(pcase (list 1 (list 2 3)) ((a (b c)) c))
;; => pcase--macroexpand: Unknown a pattern: (a (b c))
;; Oh, I recall, pcase behaves differently than normal
;; pattern-matching, I must quote everything!
(pcase-lambda '(a (b c)) c)
;; => pcase--macroexpand: Unknown a pattern: (a (b c))
;; Uh, isn’t ' a pattern in pcase? It errs out just the same!
(pcase '(1 (2 3)) ('(a (b c)) t)) ; => nil ; But… why aren’t they
; behaving the same? Why
; don’t I get an error?
;; Oh I recall! I must use “`”:
(pcase '(1 (2 3)) (`(,a (,b ,c)) t)) ; => t (It Works!™)
So:
(pcase-lambda `(,a (,b ,c)) c) ; => Wrong type argument: symbolp, (\, a)
;; Uh, isn’t that the correct way to use pcase?
;; Let’s go back before, when it unexpectedly didn’t err, wasn’t I
;; right?
(pcase '(a (b c)) ('(a (b c)) t)) ; => t (so indeed, I was right)
(pcase 'a ('a t)) ; => t (indeed, when you quote you compare symbols,
; not values (nor bind anything))
;; So let’s try the simplest case:
(pcase-lambda 'a a) ; => (lambda (quote a) a)
;; Oh, if I’m right, that “quote” isn’t a quotation, but an argument
((lambda (quote a) a) 1 2) ; => 2 (it seems so)
((lambda (quote a) a) 'a)
;; => Wrong number of arguments: (lambda (quote a) a), 1
;; it *is* so
;; Then, if I’m right, the correct usage is:
(pcase-lambda (a) a) ; => (lambda (a) a) ; okay it returns clean
; lambdas if possible
(pcase-lambda (a (2 3)) a)
;; => (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)
((pcase-lambda (a '(2 3)) a) 1 (list 2 3))
;; => Invalid function: (pcase-lambda (a (quote (2 3)) a))
;; oh, there must be a special elisp-related reason why even if it does
;; return a lambda it can’t be used right away (maybe because it’s a
;; lisp-2?). Since `lambda' doc says it’s “self-quoting” and “may be
;; used as a function”, and that’s not true the same way here, it should
;; be said.
(funcall (pcase-lambda (a '(2 3)) a) 1 (list 2 3))
=> 1
;; 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.
(funcall (pcase-lambda (a '(2 3)) a) 1 (list 1 1))
;; Okay it’s not normal, it should give an error, or returns nil, I think.
(funcall (pcase-lambda (a '(b c)) c) 1 '(2 3)) ; let’s see what’s wrong
;; => Symbol’s value as variable is void: c
;; c isn’t bound?
;; Maybe quoting, again?
(funcall (pcase-lambda (a `(,b ,c)) c) 1 '(2 3)) ; => 3 (yes, quoting)
;; Let’s try again not to match:
(funcall (pcase-lambda (`(1 ,b 3)) a) 2)
;; => let*: Wrong type argument: listp, 2
;; Cool! *Now* it gives errors!
(funcall (pcase-lambda (`(1 ,b 3)) b) '(2))
;; => nil (okay… I don’t understand)
(funcall (pcase-lambda (`(1 ,b 3)) b) '(1 2 4))
;; => 2 (mmmh… seems lazy on arity and eq-uality… but picky 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))))
;; => (2 nil) (indeed, it doesn’t give a fuck about arity)
(funcall (pcase-lambda (`(1 (2 ,c 4 . 5))) c) '(1 (2 . 3)))
;; => let*: Wrong type argument: listp, 3
;; But it want sequences to be the right type
(funcall (pcase-lambda (`(1 (2 ,c 4 . 5))) c) '(1 (2 3 . 4)))
;; => let*: Wrong type argument: listp, 4
;; Even when it doesn’t need to, apparently, so it’s not even laziness
(funcall (pcase-lambda (`(1 (2 ,c (4 5)))) c) '(1 (2 3))) ; => 3
(funcall (pcase-lambda (`(1 (2 ,c (4 5)))) c) '(1 (2 3 [4 5])))
;; => let*: Wrong type argument: listp, [4 5]
;; Yeah, sequences types, only that (it should have used “elt”, so it
;; would have always worked (you then only need to have the same
;; sequences tree structure)
;; let’s try out how that acts on arglist features
(funcall (pcase-lambda (a `(&rest ,rest)) rest) 1 2 3)
;; => 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
;; Ah indeed, to do that it would be:
(funcall (pcase-lambda (a &rest rest) rest) 1 2 3) ; => (2 3)
;; yes &rest works
;; So, I want, with correct args:
(funcall (pcase-lambda (a `(&rest ,rest)) rest) 1 '(2 3)) ; => 3
;; UH???
(funcall (pcase-lambda (a `(,b ,c &rest ,rest)) rest) 1 '(2 3 4 5 6 7))
;; => 5 (okay something strange is going)
(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))
;; => nil => nil (what?)
(funcall (pcase-lambda (a `(,b ,c &optional ,opt)) opt) 1 '(2 3 4 5))
;; => 5 (there’s one argument too much)
;; Don’t 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)) ; => (2 3 5)
;; Is seems to >< so in the end it doesn’t work?
(funcall (pcase-lambda (a &optional ,opt) (cons a opt))
1 '(2 3 4 5))
;; => pcase--macroexpand: Unknown , pattern: (\, opt)
(funcall (pcase-lambda (a &optional opt) (cons a opt))
1 2) ; => (1 . 2)
(funcall (pcase-lambda (a &optional opt) (cons a opt))
1) ; => (1)
;; Isn’t behavior not supposed to change depending on nesting?
(apply (pcase-lambda (a (b (c))) (cons a c)) '(a (b (c))))
;; => pcase--macroexpand: Unknown b pattern: (b (c))
;; It seems it becomes real `pcase' only from second level:
(apply (pcase-lambda (a `(,b (,c))) (cons a c)) '(a (b (c))))
;; => (a . c)
;; 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…
#+END_SRC
- Re: Replace trivial pcase occurrences in the Emacs sources, (continued)
- Re: Replace trivial pcase occurrences in the Emacs sources, Clément Pit-Claudel, 2018/10/31
- Re: Replace trivial pcase occurrences in the Emacs sources, Michael Heerdegen, 2018/10/30
- Re: Replace trivial pcase occurrences in the Emacs sources, Stefan Monnier, 2018/10/30
- Re: Replace trivial pcase occurrences in the Emacs sources, Michael Heerdegen, 2018/10/30
- Re: Replace trivial pcase occurrences in the Emacs sources, Stefan Monnier, 2018/10/30
- Re: Replace trivial pcase occurrences in the Emacs sources, Alan Mackenzie, 2018/10/30
- Re: Replace trivial pcase occurrences in the Emacs sources, Stefan Monnier, 2018/10/30
- Re: Replace trivial pcase occurrences in the Emacs sources, Alan Mackenzie, 2018/10/30
- Re: Replace trivial pcase occurrences in the Emacs sources, Andy Moreton, 2018/10/30
- Re: Replace trivial pcase occurrences in the Emacs sources, Andy Moreton, 2018/10/29
- pcase-lambda usage [Was: Re: Replace trivial pcase occurrences in the Emacs sources],
Garreau\, Alexandre <=
- Re: Replace trivial pcase occurrences in the Emacs sources, Van L, 2018/10/30
- Re: Replace trivial pcase occurrences in the Emacs sources, Stefan Monnier, 2018/10/30
Re: Replace trivial pcase occurrences in the Emacs sources, John Wiegley, 2018/10/23
Re: Replace trivial pcase occurrences in the Emacs sources, Stefan Monnier, 2018/10/23