June 20, 2020
On 6/20/20 5:49 AM, Stanislav Blinov wrote:
> On Saturday, 20 June 2020 at 04:34:42 UTC, H. S. Teoh wrote:
>> On Fri, Jun 19, 2020 at 09:14:30PM -0400, Andrei Alexandrescu via Digitalmars-d wrote: [...]
>>> One good goal for std.v2020 would be to forego autodecoding throughout.
>> [...]
>>
>> Another could be to fix up the range API -- i.e, reconsider the ugliness that is .save, now that D has copy ctors.
> 
> How do you see that? Fundamentally, what have copy ctors changed in this regard?

I've been thinking of this for a while. The fact that input ranges must buffer one element (i.e. make front() callable several times) has been a gauche choice.

Input ranges should have only one API:

bool fetchNext(T& target);

Fill the user-provided target with the next element and return true. At the end of the range, return false and leave the target alone.

I'd considered this design back in the day but I was annoyed that it required a copy for ranges that in fact are better than input ranges (e.g. arrays etc).

It's still a disadvantage, but over the years I realized there are not that many algorithms that only work on input ranges so we could make-do. Of those for which efficiency is a concern, they could easily be specialized for forward ranges.
June 20, 2020
On 6/20/20 6:03 AM, Petar Kirov [ZombineDev] wrote:
> On Saturday, 20 June 2020 at 09:49:38 UTC, Stanislav Blinov wrote:
>> On Saturday, 20 June 2020 at 04:34:42 UTC, H. S. Teoh wrote:
>>> On Fri, Jun 19, 2020 at 09:14:30PM -0400, Andrei Alexandrescu via Digitalmars-d wrote: [...]
>>>> One good goal for std.v2020 would be to forego autodecoding throughout.
>>> [...]
>>>
>>> Another could be to fix up the range API -- i.e, reconsider the ugliness that is .save, now that D has copy ctors.
>>
>> How do you see that? Fundamentally, what have copy ctors changed in this regard?
> 
> I suppose that postblit has had various implementation and language design issues (which copy constructors address), which prevented them from being a reliable alternative to save(). This and probably also class support.

That's not the problem. There'd need to be an API distinction between the two that is introspectable. Many input ranges should be copyable, which makes them indistinguishable from forward range by means of copy ctor.
June 20, 2020
On 6/20/20 6:43 AM, Paul Backus wrote:
> On Saturday, 20 June 2020 at 04:34:42 UTC, H. S. Teoh wrote:
>> On Fri, Jun 19, 2020 at 09:14:30PM -0400, Andrei Alexandrescu via Digitalmars-d wrote: [...]
>>> One good goal for std.v2020 would be to forego autodecoding throughout.
>> [...]
>>
>> Another could be to fix up the range API -- i.e, reconsider the ugliness that is .save, now that D has copy ctors.
>>
>>
>> T
> 
> Also, switch from `void popFront()` to `typeof(this) rest`, so that we can have `const` and `immutable` ranges.

Yah, tail() would be necessary too. popFront needs to stay because it doesn't copy the range.
June 20, 2020
On 6/20/20 9:01 AM, Petar Kirov [ZombineDev] wrote:
> On Saturday, 20 June 2020 at 12:30:43 UTC, Stanislav Blinov wrote:
>> On Saturday, 20 June 2020 at 11:20:13 UTC, Petar Kirov [ZombineDev] wrote:
>>
>>> As far as I know, back when the ranges API was worked on postblit ctors didn't work reliably, so .save() was the only option.
>>
>> Funny that, at the moment it's the copy ctors that aren't working reliably (e.g. they're not working at all with arrays). :)
> 
> You mean that array.dup doesn't work if the element type uses copy constructors? I haven't checked so I guess it's a problem indeed, though this shouldn't affect the ranges themselves (i.e. array slices).

I thought this was fixed a while ago.

June 20, 2020
On Saturday, 20 June 2020 at 14:51:32 UTC, Andrei Alexandrescu wrote:
> On 6/20/20 5:49 AM, Stanislav Blinov wrote:
>> On Saturday, 20 June 2020 at 04:34:42 UTC, H. S. Teoh wrote:
>>> On Fri, Jun 19, 2020 at 09:14:30PM -0400, Andrei Alexandrescu via Digitalmars-d wrote: [...]
>>>> One good goal for std.v2020 would be to forego autodecoding throughout.
>>> [...]
>>>
>>> Another could be to fix up the range API -- i.e, reconsider the ugliness that is .save, now that D has copy ctors.
>> 
>> How do you see that? Fundamentally, what have copy ctors changed in this regard?
>
> I've been thinking of this for a while. The fact that input ranges must buffer one element (i.e. make front() callable several times) has been a gauche choice.

I think there's a bit of conflation going on here. Being able to call front() several times does not necessitate the *range* being buffering. After all, the range, generally speaking, need not store any data at all. Buffering comes as a necessity for some *data sources*, which are either slow to fetch the data, or simply don't have it yet, not unlike the one you're showing below:

> Input ranges should have only one API:
>
> bool fetchNext(T& target);
>
> Fill the user-provided target with the next element and return true. At the end of the range, return false and leave the target alone.

For anything other than PODs, this API is less efficient and forces the callers to, effectively, do busy work (i.e. boilerplate just to facilitate language plumbing).

while (!range.empty)
{
    dataStructure.emplace(range.front); // *
    range.popFront();
}

As far as the range API itself is concerned, the above line (*), under Walter's "moving" proposal, is *at most* a constructor call and a move-construct (or just the constructor call, in a good optimizing compiler with the right data structure implementation), and *no* (zero!) destructor calls. Mind you, the `front` need not, necessarily, be returning a copy. Consider e.g. a range consuming a socket: it'd just construct the element out of buffered data and return it with NRVO (IOW, construct it directly on caller's stack, or, potentially, straight in the `dataStructure`).

Versus this:

while (true)
{
    ElementType tmp = ElementType.init;
    if (range.fetchNext(tmp)) // That's a copy-assign, i.e. a (redundant) dtor + copy
        dataStructure.emplace(move(tmp)); // one (or two) move-constructs
    else
        break;
} // redundant dtor

I am by no means an expert on compilers, but something tells me that eliding redundant calls and copies from *that* isn't a task for the light-hearted.

I'll take a "buffering" API over that any day. Not that the `fetchNext` API doesn't have its uses (it absolutely does), I just don't think it's a good candidate for ranges.
June 20, 2020
On Saturday, 20 June 2020 at 14:54:49 UTC, Andrei Alexandrescu wrote:
> On 6/20/20 9:01 AM, Petar Kirov [ZombineDev] wrote:

>> You mean that array.dup doesn't work if the element type uses copy constructors? I haven't checked so I guess it's a problem indeed, though this shouldn't affect the ranges themselves (i.e. array slices).
>
> I thought this was fixed a while ago.

Huh?

https://github.com/dlang/druntime/pull/3139#discussion_r441924024

:)
June 20, 2020
On Saturday, 20 June 2020 at 14:51:32 UTC, Andrei Alexandrescu wrote:
> On 6/20/20 5:49 AM, Stanislav Blinov wrote:
>> On Saturday, 20 June 2020 at 04:34:42 UTC, H. S. Teoh wrote:
>>> On Fri, Jun 19, 2020 at 09:14:30PM -0400, Andrei Alexandrescu via Digitalmars-d wrote: [...]
>>>> One good goal for std.v2020 would be to forego autodecoding throughout.
>>> [...]
>>>
>>> Another could be to fix up the range API -- i.e, reconsider the ugliness that is .save, now that D has copy ctors.
>> 
>> How do you see that? Fundamentally, what have copy ctors changed in this regard?
>
> I've been thinking of this for a while. The fact that input ranges must buffer one element (i.e. make front() callable several times) has been a gauche choice.
>
> Input ranges should have only one API:
>
> bool fetchNext(T& target);
>
> Fill the user-provided target with the next element and return true. At the end of the range, return false and leave the target alone.
>
> I'd considered this design back in the day but I was annoyed that it required a copy for ranges that in fact are better than input ranges (e.g. arrays etc).
>
> It's still a disadvantage, but over the years I realized there are not that many algorithms that only work on input ranges so we could make-do. Of those for which efficiency is a concern, they could easily be specialized for forward ranges.

Most of the algorithms in Phobos don't call front() multiple times and they make a copy. It is really annoying as this prevents using non copyable types or those that need to be moved, eg similar to unique_ptr. What you are suggesting doesn't sound all that better, it just cements what is already common place in Phobos into the API. It's usually not algorithms that are concerned with efficiency, but rather the type. If you implement an algorithm that you think doesn't need to be efficient but it does need to be for the type I am using it with, I don't think that'd any better than the situation we already have.
June 20, 2020
On Saturday, 20 June 2020 at 17:02:56 UTC, Avrina wrote:

> Most of the algorithms in Phobos don't call front() multiple times and they make a copy. It is really annoying as this prevents using non copyable types or those that need to be moved, eg similar to unique_ptr. What you are suggesting doesn't sound all that better, it just cements what is already common place in Phobos into the API.

On the contrary. Non-copyable types are *the* target for a hypothetical fetchNext(ref T target). Which would be @system, and you'd be calling it with an uninitialized value.
Although a one-pass consuming range could also be built with the `moveFront` interface.

That said, some algorithms do indeed require an improvement (toward moving instead of copying). `sort` just recently got such improvement courtesy of MoonlightSentinel.
June 20, 2020
On Saturday, 20 June 2020 at 14:51:32 UTC, Andrei Alexandrescu wrote:
> [snip]
>
> I've been thinking of this for a while. The fact that input ranges must buffer one element (i.e. make front() callable several times) has been a gauche choice.
>
> Input ranges should have only one API:
>
> bool fetchNext(T& target);
>
> Fill the user-provided target with the next element and return true. At the end of the range, return false and leave the target alone.
>
> I'd considered this design back in the day but I was annoyed that it required a copy for ranges that in fact are better than input ranges (e.g. arrays etc).
>
> It's still a disadvantage, but over the years I realized there are not that many algorithms that only work on input ranges so we could make-do. Of those for which efficiency is a concern, they could easily be specialized for forward ranges.

That similar to what Steven Schveighoffer did in dcollections with cursors.

https://github.com/schveiguy/dcollections/blob/master/concepts.txt
June 20, 2020
On 6/20/20 8:30 AM, Stanislav Blinov wrote:
> On Saturday, 20 June 2020 at 11:20:13 UTC, Petar Kirov [ZombineDev] wrote:
> 
>> As far as I know, back when the ranges API was worked on postblit ctors didn't work reliably, so .save() was the only option.
> 
> Funny that, at the moment it's the copy ctors that aren't working reliably (e.g. they're not working at all with arrays). :)
> 
>> If it had worked, we could require that non-forward ranges are non-copyable.
> 
> I can imagine this would be quite some work to adapt all of Phobos to *that*. I mean, things like
> 
> auto rem = makeSomeInputRange.find!pred;
> auto flt = makeSomeInputRange.filter!pred;
> 
> Those would not compile. They could be made to compile by `move`ing the argument for the return. But then you still won't be able to pass those results around, unless via a refRange or `move`.

You shouldn't need move for rvalues, which those should be. Algorithms that support non-forward ranges should be able to cope with using move where required on their parameters.

There have been a few libraries released that use non-copyable structs to model things that making copies of will prove problematic. For instance, file handles. And people seem to be able to use these reasonably well.

But the larger point is that true input-only ranges are rare. The only problem is for classes, since you cannot invoke a copy constructor on those.

It would be a drastic change, I don't see it happening. But the status quo of save() is pretty bad as well.

-Steve