* How to extract bindings from `pcase-let*`? @ 2021-03-15 1:43 Okam 2021-03-15 15:38 ` Stefan Monnier 0 siblings, 1 reply; 6+ messages in thread From: Okam @ 2021-03-15 1:43 UTC (permalink / raw) To: help-gnu-emacs Hello, I would like to extract the bindings created by `pcase-let*` to use them in a `setq` form. Is there a good way of doing this? Previously, I was using `macroexpand` on `pcase-let*` and extracting the bindings from the list, but after the expansion produced by `pcase-let*` changed recently, I realized that this way assumes too much. Thank you. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: How to extract bindings from `pcase-let*`? 2021-03-15 1:43 How to extract bindings from `pcase-let*`? Okam @ 2021-03-15 15:38 ` Stefan Monnier 2021-03-16 1:50 ` Okam 0 siblings, 1 reply; 6+ messages in thread From: Stefan Monnier @ 2021-03-15 15:38 UTC (permalink / raw) To: help-gnu-emacs > I would like to extract the bindings created by `pcase-let*` to use them > in a `setq` form. Is there a good way of doing this? Sending me a patch to `pcase.el` which does that? > Previously, I was using `macroexpand` on `pcase-let*` and extracting the > bindings from the list, but after the expansion produced by `pcase-let*` > changed recently, I realized that this way assumes too much. Interesting. Can you show me the code you used? Stefan ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: How to extract bindings from `pcase-let*`? 2021-03-15 15:38 ` Stefan Monnier @ 2021-03-16 1:50 ` Okam 2021-03-16 2:45 ` Stefan Monnier 0 siblings, 1 reply; 6+ messages in thread From: Okam @ 2021-03-16 1:50 UTC (permalink / raw) To: help-gnu-emacs On 3/15/21 11:38 AM, Stefan Monnier wrote: > >> I would like to extract the bindings created by `pcase-let*` to use them >> in a `setq` form. Is there a good way of doing this? > > Sending me a patch to `pcase.el` which does that? OK. I wanted to see if there was an intentional entry point first. >> Previously, I was using `macroexpand` on `pcase-let*` and extracting the >> bindings from the list, but after the expansion produced by `pcase-let*` >> changed recently, I realized that this way assumes too much. > > Interesting. Can you show me the code you used? This is the simple code I used: (defun loopy-pcase--get-variable-values (var val) "Destructure VAL according to VAR using `pcase'. Return a list of 2 sub-lists: (1) the needed generated variables and (2) the variables actually named in VAR. VAR should be a normal `pcase' destructuring pattern, such as \"`(a . ,b)\" or \"`(1 2 3 . ,rest)\"." ;; Using `pcase-let*' as an interface, since it is a public function. ;; `pcase' knows to not assign variables if they are unused, so we pass ;; back in `var' (a backquoted list) so that it thinks the variables ;; are used. ;; ;; This will give a form like ;; (let* (temp-vars) (let (actual-vars) VAR)) ;; ;; NOTE: Named variables might be in reverse order. Not sure if this is ;; reliable behavior. (pcase-let* ((`(let* ,temp-vars (let ,true-vars . ,_)) (macroexpand `(pcase-let* ((,var ,val)) ,var)))) (list temp-vars true-vars))) It assumes too much, and is not resilient to changes in how `pcase-let*` expands. I don't fully understand the code of Pcase yet, and failed to find something like Dash's `dash--match`, which returns a list of bindings that can be substituted into forms like `setq` or `let*`. I am doing this to attempt to optionally use Pcase for destructuring in a macro that I am writing. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: How to extract bindings from `pcase-let*`? 2021-03-16 1:50 ` Okam @ 2021-03-16 2:45 ` Stefan Monnier 2021-03-17 1:33 ` Okam 0 siblings, 1 reply; 6+ messages in thread From: Stefan Monnier @ 2021-03-16 2:45 UTC (permalink / raw) To: help-gnu-emacs > (pcase-let* ((`(let* ,temp-vars (let ,true-vars . ,_)) > (macroexpand `(pcase-let* ((,var ,val)) ,var)))) > (list temp-vars true-vars))) Indeed it's not reliable: for a case like (pcase-let (((or `(inorder ,x ,y) `(reverse ,y ,x)) FOO)) (cons x y)) it will generate code comparable to (if (eq (car FOO) 'inorder) (cons (nth 1 FOO) (nth 2 FOO)) (cons (nth 2 FOO) (nth 1 FOO)) [ Just obfuscated with gensym'd vars and such ] And if you replace the trivial (cons x y) with something less trivial it will turn into something comparable to: (let ((body (lambda (x y) <something-less-trivial>))) (if (eq (car FOO) 'inorder) (funcall body (nth 1 FOO) (nth 2 FOO)) (funcall body (nth 2 FOO) (nth 1 FOO)))) so it's far from clear how to represent this kind of code in the form of "a list of bindings that can be substituted into forms like `setq` or `let*`" OTOH, I think `pcase.el` could offer a function which could take a "buildbody" function as argument, where that buildbody would receive something like the list (x y) and would be expected to return the (lambda (x y) <something-less-trivial>) to use. So you could pass it a "buildbody" function which takes the list (x y) and returns something like (lambda (tmp1 tmp2) (setq x tmp1 y tmp2)), which I expect would do more or less what you want. > I am doing this to attempt to optionally use Pcase for destructuring in > a macro that I am writing. `pcase-let` already does destructuring, so could you explain in a bit more detail what kind of destructuring you're looking for (and why)? Stefan ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: How to extract bindings from `pcase-let*`? 2021-03-16 2:45 ` Stefan Monnier @ 2021-03-17 1:33 ` Okam 2021-03-17 2:59 ` Stefan Monnier 0 siblings, 1 reply; 6+ messages in thread From: Okam @ 2021-03-17 1:33 UTC (permalink / raw) To: help-gnu-emacs On 3/15/21 10:45 PM, Stefan Monnier wrote: > >> (pcase-let* ((`(let* ,temp-vars (let ,true-vars . ,_)) >> (macroexpand `(pcase-let* ((,var ,val)) ,var)))) >> (list temp-vars true-vars))) > > Indeed it's not reliable: for a case like > > (pcase-let (((or `(inorder ,x ,y) `(reverse ,y ,x)) FOO)) > (cons x y)) > > it will generate code comparable to > > (if (eq (car FOO) 'inorder) > (cons (nth 1 FOO) (nth 2 FOO)) > (cons (nth 2 FOO) (nth 1 FOO)) > > [ Just obfuscated with gensym'd vars and such ] > > And if you replace the trivial (cons x y) with something less trivial it > will turn into something comparable to: > > (let ((body (lambda (x y) <something-less-trivial>))) > (if (eq (car FOO) 'inorder) > (funcall body (nth 1 FOO) (nth 2 FOO)) > (funcall body (nth 2 FOO) (nth 1 FOO)))) > > so it's far from clear how to represent this kind of code in the form of > "a list of bindings that can be substituted into forms like `setq` or > `let*`" > > OTOH, I think `pcase.el` could offer a function which could take > a "buildbody" function as argument, where that buildbody would receive > something like the list (x y) and would be expected to return the > (lambda (x y) <something-less-trivial>) to use. So you could pass it a "buildbody" > function which takes the list (x y) and returns something like > (lambda (tmp1 tmp2) (setq x tmp1 y tmp2)), which I expect would do more > or less what you want. > >> I am doing this to attempt to optionally use Pcase for destructuring in >> a macro that I am writing. > > `pcase-let` already does destructuring, so could you explain in a bit > more detail what kind of destructuring you're looking for (and why)? > > > Stefan > > I am writing a looping macro similar to `cl-loop`, and have allowed for destructuring in accumulation clauses. Here are some examples: ;; Summing the nth elements of arrays: ;; => (8 10 12 14 16 18) (loopy (list (list-elem1 list-elem2) '(([1 2 3] [4 5 6]) ([7 8 9] [10 11 12]))) (sum [sum1 sum2 sum3] list-elem1) (sum [sum4 sum5 sum6] list-elem2)) ;; Or, more simply: ;; => (8 10 12 14 16 18) (loopy (list list-elem '(([1 2 3] [4 5 6]) ([7 8 9] [10 11 12]))) (sum ([sum1 sum2 sum3] [sum4 sum5 sum6]) list-elem)) ;; Separate the elements of sub-list: ;; => ((1 3) (2 4)) (loopy (list i '((1 2) (3 4))) (collect (elem1 elem2) i)) There is a built-in destructuring system for this in the macro, but a user requested a way to use other destructuring systems, such as Dash, Pcase, or `seq-let`. To do this for accumulation, I don't want to actually assign the values determined by Dash or Pcase to the variables named by the user. Instead, I want to accumulate those values into the named variables. Here is an example of the macro that I tested with Pcase (before I realized my mistake) ;; => ((1 4) (3 6)) (loopy (flag pcase) (list elem '((1 (2 3)) (4 (5 6)))) (collect `(,a (,_ ,b)) elem)) in which the `collect` expression expands into something like (setq a (append a (list some-value-from-elem))) (setq b (append b (list some-other-value-from-elem))) where `some-value-from-elem` and `some-other-value-from-elem` are determined by Pcase, Dash, or other destructuring systems. Do you think that this is doable using Pcase? A link to the macro: https://github.com/okamsn/loopy ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: How to extract bindings from `pcase-let*`? 2021-03-17 1:33 ` Okam @ 2021-03-17 2:59 ` Stefan Monnier 0 siblings, 0 replies; 6+ messages in thread From: Stefan Monnier @ 2021-03-17 2:59 UTC (permalink / raw) To: help-gnu-emacs > ;; => ((1 4) (3 6)) > (loopy (flag pcase) > (list elem '((1 (2 3)) (4 (5 6)))) > (collect `(,a (,_ ,b)) elem)) > > in which the `collect` expression expands into something like > > (setq a (append a (list some-value-from-elem))) > (setq b (append b (list some-other-value-from-elem))) > > where `some-value-from-elem` and `some-other-value-from-elem` are > determined by Pcase, Dash, or other destructuring systems. > > Do you think that this is doable using Pcase? You could do something like: (pcase--u `((,(pcase--match INCOMING (pcase--macroexpand PATTERN)) ,(lambda (vars) `(progn . ,(mapcar (lambda (v) `(push ,(cadr v) ,(car v))) vars)))))) where INCOMING is the incoming data (`elem` in your above example). As the `--` in there suggest, this is digging into pcase's internals. I think this code will work with Emacs-28 but the `cadr` would need to be replaced with `cdr` in earlier versions. Also, this does a `pcase` rather than a `pcase-let` so it will just silently do nothing if the pattern doesn't match. You can force the `pcase-let` semantics with something like: (pcase--u `((,(pcase--match INCOMING (pcase--macroexpand `(or ,PATTERN pcase--dontcare))) ,(lambda (vars) `(progn . ,(mapcar (lambda (v) `(push ,(cadr v) ,(car v))) vars)))))) See IELM session below. I suggest you `M-x report-emacs-bug` to request that this functionality be made accessible without having to rely on internals (and put me in the `X-Debbugs-Cc` when you do that). Stefan PS: I used `push` rather than your `setq+append+list` since it is algorithmically much better behaved (linear instead of quadratic complexity). ELISP> (pcase--u `((,(pcase--match 'elem (pcase--macroexpand '(or `(,a (,_ ,b)) pcase--dontcare))) ,(lambda (vars) `(progn . ,(mapcar (lambda (v) `(push ,(cadr v) ,(car v))) vars)))))) (progn (ignore (consp elem)) (let* ((x1507 (car-safe elem)) (x1508 (cdr-safe elem))) (progn (ignore (consp x1508)) (let* ((x1509 (car-safe x1508))) (progn (ignore (consp x1509)) (let* ((x1511 (cdr-safe x1509))) (progn (ignore (consp x1511)) (let* ((x1512 (car-safe x1511)) (x1513 (cdr-safe x1511))) (progn (ignore (null x1513)) (let* ((x1514 (cdr-safe x1508))) (progn (ignore (null x1514)) (progn (push x1512 b) (push x1507 a))))))))))))) ELISP> ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2021-03-17 2:59 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2021-03-15 1:43 How to extract bindings from `pcase-let*`? Okam 2021-03-15 15:38 ` Stefan Monnier 2021-03-16 1:50 ` Okam 2021-03-16 2:45 ` Stefan Monnier 2021-03-17 1:33 ` Okam 2021-03-17 2:59 ` Stefan Monnier
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).