On Thursday, 17 May 2012 at 05:48:44 UTC, Nick Sabalausky wrote:
- A range is not a collection, it's a *view* of a collection
(or of
something else). This is a necessary distinction because ranges
and
collections work in fundamentally different ways: A range is,
*by necessity*
consumed as it's iterated - that's what popFront *does*, that's
fundamentally how ranges work. For many collections, OTOH, it
makes *no*
sense to consume the collection while iterating it. Thus, range
!=
collection, it's a view of a collection.
- Ranges are D's answer to iterators. I don't think people used
to iterators
from other languages would expect their iterator to magically
reset itself
after being used. So I see no reason why they would expect a
range (ie, an
iterator-pair-with-benefits) to behave differently than that.
- D's arrays conflate the ideas of "collection" and "range",
hence the odd
edge case Era pointed out, and hence the "need" for foreach to
automatically
make a copy. But in my (not super-extensive) experience
creating ranges,
I've found that to be a problematic pattern (due to the
fundamental
distinction between a range and a collection), and learned to
prefer making
my iterable things *return* a range, rather than actually *be*
ranges.
- Admittedly, it would be annoying if foreach had to be used
like this on
all collections: "foreach(e; myArray.rangeOf)", so I guess it
would make
sense for a range to automatically be obtained when foreach-ing
over a
collection. However, I'm still not 100% sold on the current
method of doing
that (making foreach automatically copy struct-based ranges),
partly because
of the questionable implications it has for input ranges, and
partly because
(for struct-ranges) it leaves no way to access the range that's
actually
being iterated.
- At the very least, perhaps input ranges just shouldn't be
allowed to be
structs? After all, structs are intended to be copied around,
but input
ranges, by definition, can't have their current state copied.
I would be very much in support of having ranges and containers
be distinct, with a standard way to get a range from a container.
Something very similar is done in C#, where containers have a
getEnumerator() method. The enumerator itself has a Current
property and moveNext method (similar to front and popFront of a
range), and thus is consumed as you use it. In my experience,
this system works very well.
Another advantage of giving arrays a method to obtain a range
instead of them being a range themselves is that using foreach on
a const/immutable array would work as expected without having to
perform a slice to make it work. I would even go so far as to say
that having an array BE a range really doesn't make any sense,
conceptually.