Forward ranges in Phobos v2

Steven Schveighoffer schveiguy at gmail.com
Wed Nov 3 15:56:07 UTC 2021


On 11/3/21 11:03 AM, Andrei Alexandrescu wrote:
> On 2021-11-02 19:07, Paul Backus wrote:
>> On Tuesday, 2 November 2021 at 21:58:20 UTC, Andrei Alexandrescu wrote:
>>> On 2021-11-02 17:44, Dukc wrote:
>>>>
>>>>> What's a value range?
>>>>
>>>> Opposite of a reference range - copying implies `save()`.
>>>
>>> Yah, one simple improvement we could make is to assume all forward 
>>> ranges copy their iteration state when copying the range. Then input 
>>> ranges do NOT do that, i.e. all copies of an input range refer to the 
>>> same stream and iterate it together (advancing one advances all).
>>>
>>> The differentiation can be made with a nested enum tag:
>>>
>>> struct MyInputRange {
>>>     enum inputRangeTag = true;
>>>     ...
>>> }
>>>
>>> Client code can inspect R.inputRangeTag to figure whether the range 
>>> is input (if present) or forward (if missing).
>>
>> Not sure this is the best idea--it means new-style algorithms will 
>> silently treat old-style input ranges as though they were forward 
>> ranges, which could lead to incorrect behavior at runtime. If we are 
>> going to make incompatible changes to the range API, we should do it 
>> in such a way that version mismatches are caught at compile time.
> 
> Good point. Maybe have all ranges define that enum with values true and 
> false respectively?

Yes, this is what I was trying to point out in my other post.

One thing that is possible is to change at least one of the methods 
(i.e. change the name of `front`, `popFront`, or `empty`), so it is easy 
to distinguish a v2 range from a v1 range. An enum works too, and I'd 
support that.

For sure, you need an opt-in for forward ranges because input ranges are 
the most basic type.

Thinking about this some more, maybe an enum is better for another 
reason. One thing we use introspection for but can bite us is to see if 
something supports a specific interface. But what happens when what we 
expect is not what actually happens? The result is usually very 
confusing messages, or introspection that doesn't result in what we 
expect it to (i.e. some wrapper of our expected forward range only ends 
up being an input range).

Doing the check for what type of range it is different from the actual 
code expectations would not only read more cleanly (and perform better I 
think), it would push the error to the code itself. e.g. you define what 
you think is a random access range, you specify `enum rangeType = 
RangeType.RandomAccess` as a member, but forget to define `opIndex`. 
Instead of the range just being inferred as non-random-access in some 
other part of code, the compiler tells you `error, cannot call opIndex 
on type MyRange`, which gives you the exact error you need to fix the 
problem.

-Steve


More information about the Digitalmars-d mailing list