Issue with forward ranges which are reference types

Steven Schveighoffer schveiguy at yahoo.com
Wed Aug 17 12:22:46 PDT 2011


On Wed, 17 Aug 2011 14:53:53 -0400, Lars T. Kyllingstad  
<public at 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 at 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
>>>> <jmdavisProg at 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


More information about the Digitalmars-d mailing list