dash.el
dash.el copied to clipboard
destructuring idea using ...
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)
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.
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))
Note that the existing list destructuring notation is not unique to Dash, but common across Emacs packages and Lisp dialects.
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.
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.
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.