On Wed, 17 Aug 2011 14:53:53 -0400, Lars T. Kyllingstad <public@kyllingen.nospamnet> wrote:

On Wed, 17 Aug 2011 14:15:52 -0400, Steven Schveighoffer wrote:

On Wed, 17 Aug 2011 13:15:27 -0400, Lars T. Kyllingstad
<public@kyllingen.nospamnet> wrote:

On Wed, 17 Aug 2011 10:19:31 -0400, Steven Schveighoffer wrote:

On Wed, 17 Aug 2011 00:05:54 -0400, Jonathan M Davis
<jmdavisp...@gmx.com> wrote:

So, the question is, should a range-based function have the same
behavior for
all forward ranges regardless of whether they're value types or
reference types? Or should the caller be aware of whether a range is
a value type or a
reference type and call save if necessary? Or should the caller just
always
call save when passing a forward range to a function?


Probably not helpful, since the establishment seems to be set in their
opinions, but I'd recommend saying ranges are always structs, and get
rid of the save concept, replacing it with an enum solution.  The
current save regime is a fallacy, because it's not enforced.  It's as
bad as c++ const.

At the very least, let's wait until someone actually comes up with a
valid use case for reference-based forward ranges before changing any
code.  So far, all I've seen is boilerplate *RangeObject, no real
usages.

As long as most functions in std.algorithm don't take the ranges as ref
arguments, you need to use a reference-based range whenever you want
the function to consume the original range.

BTW, this is why I suggested earlier that we add a byRef range.  If you
absolutely want the function foo() to consume your range, write

   foo(byRef(myRange));

Do you have a real example besides foo which makes sense on both byRef
and by value ranges?

Well, I did try my hand at writing a parser for a wiki-style markup
language a while ago, which got its input from an input range.

It would look at the front of the range, determine what kind of element
was there (paragraph, heading, bullet list, etc.), and pass the range on
to a specialised function for dealing with that kind of element
(parseHeading(), etc.).

Of course, those functions had to consume the original range, otherwise
the same element would be repeated over and over again.  For simple
cases, this was only a matter of parseWhatever() taking the range by ref,
and everything would work nicely.  Sometimes, however, the range would be
wrapped by another range (such as Take or Until).  If I wanted these to
keep consuming the original range, I had to wrap it with byRef().

This happened often enough, and became annoying enough, that I ended up
using InputRange objects everywhere instead

The problem here seems to be that an input range is used as the base of a forward range. A forward range is much different than an input range, in that an input range destroys the data as it iterates, whereas the forward range does not.

I would say that anything that is forward range or above should never be a reference type, but anything that is strictly an input range *should* actually be a reference type (hey, I switched opinions!). The issue is that all forward ranges are input ranges.

Note that while I asked for a real example of an *algorithm*, you gave me an example of a *type* that doesn't forwar the desired behavior. I see the point however, and I think a different style of thinking is needed. That is, accepting a ref range is not guaranteed to make any range into a destructive input range, you do need a byRef range.

In any case, I think I found a more straightforward example of something that can accept either: walkLength. walkLength doesn't care whether the data gets destroyed or not, it's just counting stuff. So there is legitimate reason for something to accept both an input and forward+ range.

So what I think we need is one more isXRange to determine "is this an input range and *only* an input range?" That is, is this a *destructive* input range. In the current implementation, this would mean isInputRange!R && !isForwardRange!R

I still dislike save and how useless it is, though.

-Steve

Reply via email to