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

Lists are mutable -- aren't they supposed to be immutable? #920

Open
krader1961 opened this issue Feb 19, 2020 · 3 comments
Open

Lists are mutable -- aren't they supposed to be immutable? #920

krader1961 opened this issue Feb 19, 2020 · 3 comments

Comments

@krader1961
Copy link
Contributor

@krader1961 krader1961 commented Feb 19, 2020

~/tmp/elvish> x = [1 2 3]
~/tmp/elvish> put $@x
▶ 1
▶ 2
▶ 3
~/tmp/elvish> x[1] = 99
~/tmp/elvish> put $@x
▶ 1
▶ 99
▶ 3
~/tmp/elvish> del x[1]
Exception: value does not support element removal
[tty], line 1: del x[1]

The only mention of this I could find was in https://elv.sh/ref/language.html#list-indexing but not the earlier section that introduces lists. And the del documentation implies it works with lists as well as maps.

@krader1961

This comment has been minimized.

Copy link
Contributor Author

@krader1961 krader1961 commented Feb 19, 2020

https://elv.sh/ref/language.html#deleting-variable-or-element-del states explicitly that...

The del special command can be used to either delete variables or elements of lists and maps.

Which contradicts the text in the "list-indexing" section of that document which states that lists are immutable. Not to mention the current behavior of del throws an exception when deleting an element of a list.

This looks like the desired behavior has changed over time in an inconsistent manner. And the documentation is inconsistent because the implementation is inconsistent. So the question is whether Elvish lists should be immutable (like Python tuples) or mutable (like Python lists). I'm inclined to make them mutable; i.e., allow del to mutate the list. Yes, that is a potential source of bugs when deleting elements of a list while iterating over it. But the same is true, in theory, for deleting elements of a map. It seems to me you can't have it both ways. That is, why is deleting elements of a map okay during iteration over the map while deleting elements of a list is not?

Yes, I'm aware of the obvious answer that you can iterate over a list in this manner:

l = [1 2 3]
for x $l { put $x }

Which means that deleting either of the first two elements during the iteration raises the question of what it means to continue iterating over the list. Whereas iterating over the keys of a map requires making an explicit list of the keys:

m = [&k1=1 &k2=2]
for x [(keys $m)] { put $m[$x] }

The latter is immune to mutating, by deletion, of elements of the map due to the explicit construction of a list containing the keys of the map prior to the modification of the map. Which I can accept but then why can't I del li[0] outside of a for loop? Yes, that is a somewhat silly rhetorical question. But it illustrates why such questions are difficult to answer when designing a new language.

@xiaq

This comment has been minimized.

Copy link
Member

@xiaq xiaq commented Feb 19, 2020

Elvish's lists and maps have always been immutable, it is the variable binding that is mutable. An element assignment like li[0] = a creates a new binding for li, and does not mutate the original list. This is explained in https://elv.sh/learn/unique-semantics.html#assignment-semantics, but I should also explain that in the reference doc.

The del command does not work with list elements, the doc is wrong to say that it does. There is no efficient way to delete an element from a list as it involves moving all the elements after it.

xiaq added a commit that referenced this issue Feb 19, 2020
The command does not support deleting list elements.

This addresses #920.
@krader1961

This comment has been minimized.

Copy link
Contributor Author

@krader1961 krader1961 commented Feb 20, 2020

An element assignment like li[0] = a creates a new binding for li, and does not mutate the original list.

I understand what you're saying but that is extremely subtle. I would bet that 96% of elvish users are unaware of that behavior and will be surprised by it. I read all the elvish docs a year ago and forgot about the "unique assignment semantics". Too, the example in https://elv.sh/learn/unique-semantics.html#assignment-semantics, only applies to maps. If you replace the map example with what would seem to be an equivalent example using a list you get the expected nonsensical results:

~> l1 = [11 22 33]
~> l2 = $l1
~> put $@l1
▶ 11
▶ 22
▶ 33
~> put $@l2
▶ l
▶ 1
~> l2[1] = 44
~> put $@l2
▶ l
▶ 4
▶ 4
~> put $l2
▶ l44
~> put $l1
▶ [11 22 33]

Note that it let me mutate the string assigned to l2 by extending its length. That, too, seems inconsistent with the idea of immutable data structures.

P.S., That it is inefficient to delete an element from a list does not, by itself, seem like a good reason to disallow it. Especially when you can otherwise mutate the list by essentially binding a new instance of the list to the var via an assignment to an element of the list. It seems to me it would be more consistent to allow del but document that it will be expensive and should be avoided.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.