Range Redesign: Empty Ranges

Paul Backus snarwin at gmail.com
Thu Mar 7 23:15:02 UTC 2024


On Thursday, 7 March 2024 at 18:32:50 UTC, Steven Schveighoffer 
wrote:
> I meant the latter. A concrete range type.
>
> How does it happen? Consider that an array is a concrete type 
> that people use all the time. Why should any other range be 
> different? I've definitely stored ranges and other things as 
> type members that were voldemort types.

This doesn't answer my question.

There are lots of ways in which (built-in) arrays are different 
from other types of ranges. Most notably, an array can be used as 
either a range or a container. There are plenty of use-cases for 
creating an empty container.

What I am asking, specifically, is whether there is any use-case 
where *generic code*, given a range of some arbitrary type `R`, 
needs to create another range which both (a) has the exact 
concrete type `R`, and (b) is empty. Since that's the feature 
that's being proposed here.

(Non-generic code does not need this feature to be part of the 
range API, because it can rely on specific features of whatever 
concrete type it's working with.)

> This ability is more of a question of "do we want to add this 
> feature to ranges or not?" The feature doesn't currently exist 
> -- you can't assume that an uninitialized range is empty.

If there are no use-cases for this feature, then the answer to 
"do we want to add it" ought to be "no." That's why I'm asking 
about use-cases.

> Sometimes, we are bitten by the fact that the array is the most 
> common range, and behaves in a specific way. People depend on 
> that mechanism without realizing it, and then sometime later, 
> they decide to change the type to one that is very compatible 
> with arrays, but offers some benefit (i.e. to remove an 
> allocation). However, the new range type might behave in 
> unexpected ways, *but still compiles*.

This is a general problem with templates/macros compared to typed 
generics. Even if we get rid of this particular edge case, there 
are still dozens more that users are going to run into if they 
only test with arrays.

If we want to address this problem, I think the best thing we can 
do is to provide a standard suite of test ranges that users can 
plug into their code to uncover edge cases and bugs.

>> The main thing you lose by dropping support for reference-type 
>> ranges is interfaces. In particular, the interface inheritance 
>> hierarchy in `std.range.interfaces`, where `ForwardRange` 
>> inherits from `InputRange` and so on, cannot really be 
>> replicated using `structs` (`alias this` only goes so far).
>
> As mentioned, you can wrap these interfaces into structs, which 
> then have better lifetime tracking capabilities.

How do you implement this with structs?

     interface ForwardAssignable : InputAssignable!E, 
ForwardRange!E

Interfaces allow multiple inheritance. Structs can only have one 
`alias this` member. Maybe you're fine with giving up on this 
feature, but let's at least be honest that we *are* giving up 
features here.


More information about the Digitalmars-d mailing list