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

destructuring idea using ... #332

Open
Luis-Henriquez-Perez opened this issue Feb 29, 2020 · 6 comments
Open

destructuring idea using ... #332

Luis-Henriquez-Perez opened this issue Feb 29, 2020 · 6 comments
Labels
enhancement Suggestion to improve or extend existing behavior

Comments

@Luis-Henriquez-Perez
Copy link

A destructuring idea using ...:

(-let [(first ... last) '(1 2 3 4 5)]
  (list first last))
;; => (1 5)

(-let [(first ... second-to-last last) '(1 2 3 4 5)]
  (list first second-to-last last))
;; => (1 4 5)
@Fuco1 Fuco1 added the enhancement Suggestion to improve or extend existing behavior label Feb 29, 2020
@jirassimok
Copy link

If there's a syntax for binding the last element(s), it would also make sense to have syntax for binding the middle elements. Something like (first more... last) would be nice, but (first &rest more last) or (first &rest rest &last lat) might be cleaner to implement.

@Luis-Henriquez-Perez
Copy link
Author

Luis-Henriquez-Perez commented Sep 14, 2020

Well it might make the most sense for (first middle last) to be used.

For example:

;; What I think would be ideal
(let [(first middle last) (list 1 2 3 4 5 6)]
  (list first middle last))
;; => (1 (2 3 4 5) 6)

;; What actually happens in dash.el
(let [(first middle last) (list 1 2 3 4 5 6)]
  (list first middle last))
;; => (1 2 3)

;; What would be better notation for what actually happens
(let [(first middle last ...) (list 1 2 3 4 5 6)]
  (list first middle last))
;; => (1 2 3)

I remember when I first started using dash, it was not intutive to me that
(let [(one two three) (list 1 2 3 4 5 6)]) would take the first three elements.

However this notation is already used by dash and changing it would result in a
breaking change, so I doubt this notation would change. Though maybe there could
be a variable set to nil by default to allow this interpretation.

EDIT:

On second thought is how this is how I would expect this to work. What I said
about expecting this (1 (2 3 4 5) 6) doesn't make sense.

(let [(first middle last) (list 1 2 3 4 5 6)]
  (list first middle last))
;; =>
(1 2 (3 4 5 6))

@basil-conto
Copy link
Collaborator

Note that the existing list destructuring notation is not unique to Dash, but common across Emacs packages and Lisp dialects.

@Fuco1
Copy link
Collaborator

Fuco1 commented Sep 17, 2020

I personally like the ... syntax but it's a bit unlispy. We can go with &middle or multiple &rest uses. Although what would be the match with three of those :O

Alternatively if there is a &rest and then more than one (= n) places, we would match the last n - 1 things to those and the rest to the first place after the rest. Which makes most sense to me. If I understand it right this is what @jirassimok is proposing. I like it.

Note that we already support the syntax (a . b) where (1 2 3 4 5) would produce (let ((a 1) (b '(2 3 4 5))))

For syntax reasons we can't support (a . b c) as this won't read in Elisp.

@jirassimok
Copy link

Yes, that's what I suggested. Personally, I prefer to also include ... (or &last) as a shorthand for &rest _, so we don't need to write (&rest _ last) for the case where we just want the last element.

Another syntax that I considered was (first ,@middle last). This feels a little less intuitive and clear, but but is less verbose and fits with the common use of ,@ for splicing (i.e. `(a ,@b c) reverses the destructuring).

Even if (a . b c) were possible, I don't think it's a good idea, because it removes the straightforward syntax of (first &rest middle last . extra) for destructuring improper lists.

@Fuco1
Copy link
Collaborator

Fuco1 commented Sep 23, 2020

I would understand (foo &rest last) to only bind the rest of the items to the last. Only in case there is more than one item we would bind the "middle" items to the middle place, save for any additional places which would then be matched going backwards from the end.

For (1 2 3 4 5)

(foo &rest a)  foo = 1, a = (2 3 4 5)
(foo &rest a b)  foo = 1, a = (2 3 4), b = 5
(foo &rest a b c)  foo = 1, a = (2 3), b = 4, c = 5
(foo &rest a b c d)  foo = 1, a = (2), b = 3, c = 4, d = 5
(foo &rest a b c d e)  foo = 1, a = nil (??/error), b = 2, c = 3, d = 4, e = 5

Edit: I see, you want to specifically bind the last item only. &last seems like a good solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Suggestion to improve or extend existing behavior
Projects
None yet
Development

No branches or pull requests

4 participants