You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I ran into a problem described in this gist whereby a manually-implemented TraversableOnce saw incorrect behaviour of the reduceLeft method because the reduceLeft implementation assumes that calls to TraversableOnce.isEmpty are idempotent (i.e. they do not consume the traversable).
This is, of course, how one would expect the method to be implemented on Iterator but I believe that the current documentation of the methods falls well short:
/** Self-documenting abstract methods. */
def foreach[U](f: A => U): Unit
def isEmpty: Boolean
def hasDefiniteSize: Boolean
I believe that the documentation should explicitly say that implementations of isEmpty should be idempotent.
Furthermore, I believe that hasDefiniteSize is absolutely not self-documenting (without understanding where it is called). For example, what does one expect the following to print?
scala> Stream.continually(1).iterator.hasDefiniteSize //prints 'false' of course
scala> List(1).iterator.hasDefiniteSize //what does this print?
A quick survey of colleagues resulted in everyone assuming that the latter should return true (it doesn't).
Note also that the behaviour of Traversable and Iterator in this case:
scala> val it = List(1).iterator; it foreach (_ => println(it.isEmpty)) //prints 'true'
scala> val it = Traversable(1); it foreach (_ => println(it.isEmpty)) //prints 'false'
The text was updated successfully, but these errors were encountered:
@Blaisorblade said:
Regarding the Traversable/Iterator difference (in the last point of the report): It is not addressed in the PR. But the case shown appears pretty understandable — I guess when the body runs, the iterator was consumed and is thus empty, while the Traversable doesn't get consumed and stays full. Annoying, but if you don't like that semantics in this case, don't use iterators I guess.
Is there a more complicated scenario where the behavior is harder to explain? Is the documentation on this behavior incomplete?
@szeiger said:
Right, I think that part simply works as expected. The difference is not due to isEmpty consuming an element. When you get to the first println, the only element of the Iterator has already been consumed (it's passed to your lambda after all), so the Iterator is empty at that point.
Similarly, the first part of this ticket (in the gist) also does the "right" thing but maybe that's because I didn't understand the reason for using a Traversable instead of a TraversableOnce there in the first place. If you implement a Traversable even though your source can only be traversed once and you do not cache the data, it will go wrong at some point. I would implement an Iterator in these cases but that's already the first recommendation in the scaladoc for TraversableOnce:
*Directly subclassing `TraversableOnce` is not recommended - instead,
* consider declaring an `Iterator` with a `next` and `hasNext` method,
* creating an `Iterator` with one of the methods on the `Iterator` object,
* or declaring a subclass of `Traversable`.
On second thought, maybe this comment should be changed to warn that the alternative of subclassing Traversable only applies if you really have a source that can be traversed repeatedly (even if all you need is the TraversableOnce part of it).
I ran into a problem described in this gist whereby a manually-implemented
TraversableOnce
saw incorrect behaviour of thereduceLeft
method because thereduceLeft
implementation assumes that calls toTraversableOnce.isEmpty
are idempotent (i.e. they do not consume the traversable).This is, of course, how one would expect the method to be implemented on
Iterator
but I believe that the current documentation of the methods falls well short:I believe that the documentation should explicitly say that implementations of
isEmpty
should be idempotent.Furthermore, I believe that
hasDefiniteSize
is absolutely not self-documenting (without understanding where it is called). For example, what does one expect the following to print?A quick survey of colleagues resulted in everyone assuming that the latter should return
true
(it doesn't).Note also that the behaviour of
Traversable
andIterator
in this case:The text was updated successfully, but these errors were encountered: