Range Redesign: Copy Semantics

Jonathan M Davis newsgroup.d at jmdavisprog.com
Mon Jan 22 02:49:23 UTC 2024


On Sunday, January 21, 2024 3:46:06 PM MST Paul Backus via Digitalmars-d 
wrote:
> On Sunday, 21 January 2024 at 19:24:20 UTC, Steven Schveighoffer
>
> wrote:
> > While I agree with most of what you wrote, there is one very
> > big problem with this. Namely, current input ranges are
> > suddenly considered to be forward ranges. Changing the
> > semantics of what it means to only define `front`, `popFront`,
> > and `empty` changes the semantics of existing code.
> >
> > And this is not something you can solve with some kind of
> > versioning. The only way I can think of is to alter the name of
> > one or more primitives to prevent accidental usage.
>
> One possibility is to change the forward range interface to
> something like
>
>      bool empty();
>      Element head();
>      typeof(this) tail();
>
> In addition to distinguishing new-style forward ranges from
> old-style ones, this interface would also allow forward ranges to
> be `const` (although iterating a `const` forward range would only
> be possible with recursion).
>
> `popFront` could then be implemented as a UFCS function:
>
>      void popFront(R)(ref R r)
>          if (std.v2.range.isForwardRange!R && isAssignable!R)
>      {
>          r = r.tail;
>      }

Well, I think that the reality of the matter here is that if we want to deal
with const and immutable properly (which is another issue that we need to be
tackling with the range API redesign), we're either going to need to switch
to a more functional-style API like this, or we're going to need to add some
sort of opTailConst to the language, or we're going to need to add some
other sort of language improvement that would allow use to convert
const(Range!Element) to Range!(const Element) generically. As things stand,
const and ranges really don't interact well together outside of fairly
limited situations (e.g. using dynamic arrays for everything), and
containers in particular get screwed by it.

So, we may very well decide that we need to move forward ranges to something
like head and tail, though I'm not terribly happy with the idea, and I'm not
sure what the performance implications are (though LDC at least could
probably make it not matter). It would also require another change to
foreach, but pretty much _anything_ we do here to fix the tail-const problem
with ranges is going to require some kind of language change.

Another problem with the head/tail approach is that we then have to come up
with names for bidirectional ranges, and all of that could get pretty
confusing. But ultimately, that can be solved.

Either way, the main things that I think needs to happen to fix the copy
semantics problems are to require that forward ranges have the same copy
semantics as dynamic arrays (at least with regards to their iteration
state), which means that save is no longer a thing, and we need basic input
ranges to be reference types, which means that they should probably have a
different API from forward ranges. I suppose that we could then change the
forward range API and leave the basic input range API alone, but given that
the current API requires caching front, which causes some problems for basic
input ranges, I'm inclined to give basic input ranges a different API. If we
then also need to change the API of forward ranges beyond removing save,
then so be it, though that will result in a lot more code having to be
changed when it's updated to use or be in Phobos v3.

- Jonathan M Davis





More information about the Digitalmars-d mailing list