digitalmars.D.learn - Must ranges have value semantics?
- Chris Wright (17/17) Dec 15 2015 I noticed that some methods in Phobos will have very different behavior
- H. S. Teoh via Digitalmars-d-learn (29/51) Dec 15 2015 This is one of the "undefined" or poorly-defined areas of D ranges. :-(
- Jesse Phillips (11/29) Dec 15 2015 Unfortunately you must make no assumption about the ranges state
I noticed that some methods in Phobos will have very different behavior
with forward ranges that have reference semantics and those that have
value semantics.
Example:
auto range = getSomeRange();
auto count = range.walkLength;
foreach (element; range) { writeln(element); }
If getSomeRange returns a forward range that is a reference type with
no .length property, walkLength will exhaust the range. The iteration
after that will never enter the loop body.
However, if getSomeRange returns a range with value semantics with
no .length property, then iteration is still possible.
I haven't found documentation about how ranges are intended to be used in
D written by people who maintain Phobos. Is it normal and expected that I
should have to call .save everywhere, manually? Was there discussion on
whether this should be the case or a published document containing the
reasoning behind the decision?
Dec 15 2015
On Tue, Dec 15, 2015 at 05:36:52PM +0000, Chris Wright via Digitalmars-d-learn
wrote:
I noticed that some methods in Phobos will have very different
behavior with forward ranges that have reference semantics and those
that have value semantics.
Example:
auto range = getSomeRange();
auto count = range.walkLength;
foreach (element; range) { writeln(element); }
If getSomeRange returns a forward range that is a reference type with
no .length property, walkLength will exhaust the range. The iteration
after that will never enter the loop body.
However, if getSomeRange returns a range with value semantics with no
.length property, then iteration is still possible.
I haven't found documentation about how ranges are intended to be used
in D written by people who maintain Phobos. Is it normal and expected
that I should have to call .save everywhere, manually? Was there
discussion on whether this should be the case or a published document
containing the reasoning behind the decision?
This is one of the "undefined" or poorly-defined areas of D ranges. :-(
Basically, in generic code, you should always use .save where you expect
to reuse the range after iterating over it. In non-generic code, of
course, you already know what the range semantics are and you don't need
to use .save, but in generic code, where the incoming range type may or
may not have reference semantics, always call .save to be on the safe
side. Value-type ranges simply return `this` in their .save method
anyway, so it doesn't hurt.
(There have been (and possibly still are) places in Phobos where .save
wasn't used, leading to subtle bugs that only manifest themselves when
user code starts passing reference-semantics ranges around. Phobos
unittests in general, unfortunately, tend to only test value-type
ranges, so this problem often gets missed. There have been attempts to
remedy this, but AFAIK there are still many unittests out there that
don't adequately verify their target functions with reference-type
ranges. Or, on a related note, anything other than arrays, which often
leads to hiding places for bugs that only show up with non-array
ranges.)
There was a discussion recently that .save may have been a
miscalculation in the design of ranges, and that forward ranges should
have done this in postblit instead of a separate method. (One should
understand, however, that at the time ranges were first defined, we
didn't have postblit semantics the way we have them today, so at that
time a .save method was possibly the best compromise given the state of
the language then.)
T
--
Never ascribe to malice that which is adequately explained by incompetence. --
Napoleon Bonaparte
Dec 15 2015
On Tuesday, 15 December 2015 at 17:36:52 UTC, Chris Wright wrote:
I noticed that some methods in Phobos will have very different
behavior with forward ranges that have reference semantics and
those that have value semantics.
Example:
auto range = getSomeRange();
auto count = range.walkLength;
foreach (element; range) { writeln(element); }
If getSomeRange returns a forward range that is a reference
type with no .length property, walkLength will exhaust the
range. The iteration after that will never enter the loop body.
However, if getSomeRange returns a range with value semantics
with no .length property, then iteration is still possible.
I haven't found documentation about how ranges are intended to
be used in D written by people who maintain Phobos. Is it
normal and expected that I should have to call .save
everywhere, manually? Was there discussion on whether this
should be the case or a published document containing the
reasoning behind the decision?
Unfortunately you must make no assumption about the ranges state
in generic code after calling a function that takes the range by
value. If you want to perform other operations after the call to
walklength, you'll need to have a new range created with save()
before that call.
The value vs reference rage creates challenges, but in practice
it will be something you occasionally hit.
I kind of feel like a range should always be taken by reference
so that it is predictable that the range will be modified if it
is value or reference semantics.
Dec 15 2015









"H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> 