Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

idea: dash destructurings #20

Closed
Luis-Henriquez-Perez opened this issue Feb 8, 2021 · 3 comments
Closed

idea: dash destructurings #20

Luis-Henriquez-Perez opened this issue Feb 8, 2021 · 3 comments

Comments

@Luis-Henriquez-Perez
Copy link
Contributor

Luis-Henriquez-Perez commented Feb 8, 2021

As I was using loopy it occurred to me that there are cases where I may want to use some of the features provided by dash's destructuring. Perhaps others using loopy might want to as well. Right now loopy and dash (not to mention cl-destructuring-bind and pcase) implement their own destructuring). What would be cool is if loopy could easily use destructuring provided by another package.

As a side-issue--not sure if this should be the responsibility of loopy, perhaps this should be a package. There should be a destructuring package that implements a cutomizable destructuring and is designed for other packages to use if they need destructuring for some purpose (like loopy does). This would make it so that packages don't each implement their own destructuring; also, it would provide a uniform destructuring experience among several packages. By customizable I mean that it should be easy to add your own "unorthodox" destructuring idioms (like dash does for example).

@okamsn
Copy link
Owner

okamsn commented Feb 15, 2021

I think this is a good idea, and have added an attempt to allow destructuring with Dash.

I am not very familiar with Dash, so please try it out. I'm sure that I missed some things.

To enable it, you can load the new package like this:

    (use-package loopy
      :straight (loopy :type git :host github :repo "okamsn/loopy"
                       :files (:defaults (:exclude "loopy-dash.el"))))

    ;; Optional support for destructuring with Dash.
    (use-package loopy-dash
      :after (loopy)
      :demand t
      :straight (loopy-dash :type git :host github :repo "okamsn/loopy"
                            :files ("loopy-dash.el")))

To use it, use (flag dash) as a macro argument to loopy:

     ;; => (((1 (2 3)) (4 (5 6))) ; whole
     ;;     (1 4)                 ; i
     ;;     (2 5)                 ; j
     ;;     (3 6))                ; k
     (loopy (flag dash)
            (loop (list elem '((1 (2 3)) (4 (5 6))))
                  (collect (whole &as i (j k)) elem)))

To use it all of the time, customize loopy-default-destructuring-function and
loopy-default-accumulation-parsing-function as described in the docs.

I will try to add pcase destructuring later, but adding the dash support was surprisingly straightforward. Hopefully pcase is similar.

@Luis-Henriquez-Perez
Copy link
Contributor Author

Luis-Henriquez-Perez commented Feb 15, 2021

I have added an attempt to allow destructuring with Dash

That's neat! I'm particularly excited about being able to extend dash's destructuring and add features like this one.

We should add the link to the relevant dash documentation as well as referencing it.

I will try to add pcase destructuring later, but adding the dash support was surprisingly straightforward. Hopefully pcase is similar.

I think it is similar. I'm not a fan of it's syntax.

EDIT: pcase is very powerful, however, is tightly integrated into emacs, is well-documented and provides the most features of all other pattern matching frameworks that I know of. The only problem I have with it is its backquote destructuring. Perhaps it is possible to write a function that converts cl-destructuring type syntax to the backquoted syntax pcase prefers. However, I'm not suggesting that you do this for the adding of pcase destructuring--add it as it is. I'm just musing about how I can extend it to do what I want.

@okamsn
Copy link
Owner

okamsn commented Feb 23, 2021

I've added destructuring with pcase and seq-let (which is pcase
underneath).

I just made the flags pcase and seq. I will update the documentation after
#26 is settled. You should only have to (require 'loopy-pcase) or
(require 'loopy-seq).

All of these destructuring methods have their own peculiarities. In particular,
pcase-let* (and, by extension, seq-let) tries to avoid binding variables
that aren't actually used, so I passed them the variable list as the let body.
This works with what I've tested, but I don't know how well it will work for
more complicated variable lists.

For example,

(seq-let (a b (c d))
    '(1 2 (3 4))
  '(a b (c d)))

(pcase-let* ((`(,a ,b (,c ,d))
              '(1 2 (3 4))))
  `(,a ,b (,c ,d)))

expands into

;; `seq-let':
(let* ((x1246 (seq--elt-safe '(1 2 (3 4)) 2))
       (x1247 (seq--elt-safe x1246 1))
       (x1248 (seq--elt-safe x1246 0))
       (x1249 (seq--elt-safe '(1 2 (3 4)) 1))
       (x1250 (seq--elt-safe '(1 2 (3 4)) 0)))
  (let ((c x1248)
        (d x1247)
        (b x1249)
        (a x1250))
    '(a b (c d))))

;; `pcase':
(let* ((x1267 (car '(1 2 (3 4))))
       (x1268 (cdr '(1 2 (3 4))))
       (x1269 (car x1268))
       (x1270 (cdr x1268))
       (x1271 (car x1270))
       (x1272 (car x1271))
       (x1273 (cdr x1271))
       (x1274 (car x1273))
       (x1275 (cdr x1273))
       (x1276 (cdr x1270)))
  (let ((c x1272)
        (d x1274)
        (b x1269)
        (a x1267))
    `(,a ,b (,c ,d))))

but I'm not sure whether this behavior is reliable or likely to change.

Example of pcase:

(ert-deftest list-destructuring ()
  (should (and (equal '(5 6)
                      (eval (quote (loopy (flag pcase)
                                          ((list `(,a . ,b)
                                                 '((1 . 2) (3 . 4) (5 . 6))))
                                          (finally-return a b)))))
               (equal '(5 (6))
                      (eval (quote (loopy (flag pcase)
                                          ((list `(,a . ,b)
                                                 '((1 2) (3 4) (5 6))))
                                          (finally-return a b)))))
               (equal '(4 5 6 7)
                      (eval (quote (loopy (flag pcase)
                                          ((list `(,a ,b ,c ,d)
                                                 '((1 2 3 4) (4 5 6 7))))
                                          (finally-return a b c d)))))
               (equal '(4 5 6)
                      (eval (quote (loopy (flag pcase)
                                          ((list `[,i ,j ,k] '([1 2 3] [4 5 6])))
                                          (return i j k))))))))

Example of seq-let:

(ert-deftest list-destructuring ()
  (should (and (equal '(5 6)
                      (eval (quote (loopy (flag seq)
                                          ((list (a &rest b)
                                                 '((1 . 2) (3 . 4) (5 . 6))))
                                          (finally-return a b)))))
               (equal '(5 (6))
                      (eval (quote (loopy (flag seq)
                                          ((list (a &rest b)
                                                 '((1 2) (3 4) (5 6))))
                                          (finally-return a b)))))
               (equal '(4 5 6 7)
                      (eval (quote (loopy (flag seq)
                                          ((list (a b c d)
                                                 '((1 2 3 4) (4 5 6 7))))
                                          (finally-return a b c d)))))
               (equal '(4 5 6)
                      (eval (quote (loopy (flag seq)
                                          ((list [i j k] '([1 2 3] [4 5 6])))
                                          (return i j k))))))))

@okamsn okamsn closed this as completed Mar 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants