-
Notifications
You must be signed in to change notification settings - Fork 72
Remove empty parameter list from iterator()
#520
Comments
and then @julienrf said "I’m OK to go back to iterator for the sake of backward compatibility." |
This reminds me of this discussion long time ago: https://groups.google.com/forum/#!topic/scala-language/RlV9O1RDmis |
The uniform access principle says that
assuming some single-threadedness guarantees*. So that means Furthermore:
I think it would be nice if we could enforce that principle with the collections redesign, provided porting efforts are acceptable. For Scala 2.13, is there a cost for changing to
|
But Also, if |
Not if we mean
That's indeed a problem, but I guess it is part of the understanding that iterators are "use once", so you could argue that
for every collection |
It is a more general problem with The only real way to solve it without breaking the inheritance structure of the collections is to allow overriding the number of empty parameter lists for a method, which seems weird. |
Oops, I'd forgotten that I agree with the uniform access principle, but I don't think that equality as a proof of uniform access works properly. Take, for instance,
These two behave identically with regard to the So the equality test seems not to be necessary since you can break it by essentially irrelevant modifications to other parts of the class. For instance, if you add or remove an Furthermore, the return value isn't even the important thing. Take, for instance,
Now, despite the fact that So certainly the equality test isn't sufficient because we need to know whether the class itself has changed or not. Since equality is neither necessary nor sufficient for determining whether something obeys the replace-me-with-a-field rule, I think we shouldn't really use it. Sometimes it will give the same answer, but it can have errors either way. Now, I think phrasing the uniform access principle in terms of whether a call is indistinguishable from field access is fine, but that leaves us with a rather unpleasant situation in inheritance hierarchies where some subsets of the hierarchy are immutable. You're basically forced to always use |
Can |
It can. Everything looks normal as long as the implicit parameter list stays implicit but you're up for a surprise if you're used to calling with without parentheses and then want to pass in an explicit
|
It seems there's an agreement to follow @odersky's strategy #520 (comment). So I'm closgin this, created #571 for |
@NthPortal thanks for speaking up, I was also testing the waters :-) I'll reopen for now. @Ichoran says
I agree with that. Can we just drop the second part in @odersky's strategy?
This would leave us with "if the operation could have been implemented as a (mutable) field". So this would mean @Ichoran remarks
I'd like to see an example here. |
@lrytz - As an example, consider |
@lrytz An example from a very different angle is |
To generalize what @Ichoran said, any method on a |
Thanks for the good examples! Do we all agree that we don't want to add Can we just say "add
|
It leads to the question what is a side effect. Referential transparency is not a useful answer here, as we're working with mutable state. Effects are IO, observable state changs (changes to local / newly allocated state is not an effect, niether is updating a cache). Exceptions are excluded ( |
Yes, please. This how I've understood and taught I dislike |
I noticed last night that |
Searching a bit in the library, there's also
And outside collections
|
Methods on |
There was a discussion on https://gitter.im/scala/collection-strawman about whether or not
IterableOnce.iterator()
/Iterable.iterator()
should have an empty parameter list, or none at all. I will do my best to record the main points of the discussion below.(mostly quotes from Gitter)
I ask "Why does
IterableOnce.iterator()
have an empty parameter list, rather than no parameter list?"@szeiger and @julienrf suggest it is because
iterator()
is a side-effecting method, in that two returnedIterator
s have distinct identities, so the method is not referentially transparent.@martijnhoekstra notes that "If everything that's not referentially transparent is a side effect, obtaining an iterator is a side-effect".
I ask "What about
toArray
? The Arrays are mutable, so while initially they would be equivalent, you could modify them so they are not".@szeiger: "Indeed, if you go by referential transparency a lot more methods would need an empty argument list. What about
size
? When used on a mutable collection it's not referentially transparent. Of course, nothing is. The distinction doesn't make any sense there. So what aboutList.size
? It is referentially transparent but that's not good enough because substituting such calls has huge performance implications. In a pure functional language the compiler can help you with that but Scala can't."@SethTisue: "My intuition likes the 'initially they would be equivalent' side of your way of phrasing it, @NthPortal. But I'm having trouble backing up my intuition with a convincing argument [...] I think my intuition is something like, require the
()
if it introduces mutation or side effects into a program that wouldn't otherwise have it. AndtoArray
doesn't do that, because you are free to write code that usesArray
but never mutates an array".@martijnhoekstra: "Neither does
iterator()
though - provided you never callnext()
[...] There is a lot less you can do with an iterator without modifying it than you can do with anArray
without modifying it though".@SethTisue: "In Scala through 2.12,
.iterator
has no parameter lists and the compiler says 'Iterator[...] does not take parameters' if you write.iterator()
. So somebody made a different decision on this years ago. Just leaving it like it was doesn't feel terrible to me. Even if you buy the argument that it ought to have the()
, I'm not sure it's worth annoying people with the change, especially since (as Stefan has pointed out) the change will become more annoying when future Scala requires the()
".@Ichoran says that "My intuition is that
()
as a way to denote referential transparency is not a good idea anyway. Instead,()
is useful as a marker for things that observably mutate the object on which the method is called. That is, for some methodbar
, the behavior ofx.bar
will be different afterx.foo()
." @SethTisue likes this intuition.The text was updated successfully, but these errors were encountered: