range.save
Jonathan M Davis via Digitalmars-d
digitalmars-d at puremagic.com
Fri Nov 27 01:20:21 PST 2015
On Friday, 27 November 2015 at 08:53:26 UTC, Joseph Rushton
Wakeling wrote:
> On Thursday, 26 November 2015 at 17:23:07 UTC, Andrei
> Alexandrescu wrote:
>> So initially I thought a simple solution would be this:
>>
>> struct MyForwardRange
>> {
>> enum bool isForward = true;
>> ... empty, front, popFront ...
>> }
>>
>> enum isForwardRange(R) =
>> is(typeof(R.isForward)) && R.isForward;
>>
>> Then there's no need for .save(), and isForward!R can be used
>> for constraints etc. Reference forward ranges are not
>> supported but then that's liberating (fewer complications),
>> rare, and easy to work around by wrapping.
>
> By "reference forward range" do you mean a reference type (per
> se) or any range that contains an internal reference type (e.g.
> a range whose private data includes a dynamic array)?
Obviously, Andrei will have to answer to know what he meant, but
with regards to ranges, I would consider a reference type to be
one where in
auto copy = range;
doing anything to copy or range does the exact same thing to the
other, because they refer to the exact same state. Something like
save is required to get a separate range where popping elements
from one will not affect the other.
Contrast that with a value type where copy and range are
completely separate. And then there are ranges where the copy
shares _some_ state with the original but not all, which as far
as save goes is pretty much the same as a reference type but
means that you can't rely on mutating one having the same effect
on the other one either (the easiest example would be a range
that would otherwise be a reference type but caches front).
Based on that view of things, dynamic arrays definitely are value
types. Obviously, if you start doing stuff that would mutate
their elements, then they aren't really value types, but if all
you're doing is iterating over them, then they're essentially
value types.
What we lose without save (or something else to replace it) is
the ability to have any range types that aren't value types (or
which essentially behave like them). So, a range which is a
dynamic array or a simple wrapper around a dynamic array is fine,
because copying the range is enough, but anything where a copy is
not the same as save would have been will no longer work. Using
postblit constructors like Sonke suggested solves _some_ of that
problem, but not all of it, since that doesn't work with classes,
and const doesn't work with postblit constructors, whereas we
could make it work with save. The loss of classes poses the
problem that non-templated functions can't really be made to work
with ranges. And the loss of reference type ranges in general
definitely is a problem for some uses cases.
But the more I look at the semantics involved, the more inclined
I am to argue that trying to have the same code work for both
value types and reference types is questionable at best. On top
of that, pure input ranges almost need to be reference types
(though they can currently work as pseudo-reference types at
least some of the time), and forward ranges really need to be
value types (at least as much as dynamic arrays are anyway) if we
don't want to have to call save everywhere.
I'm starting to think that it would be better to have pure input
ranges have to be reference types and forward ranges have to be
value types and then be very careful about which functions work
with both rather than simply treating all forward ranges like
pure input ranges that can also be copied via save.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list