June 20, 2020
On 6/20/20 2:04 PM, jmh530 wrote:
> 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

Sorry, this is not the same.

Cursors are simply a way to refer to exactly one element, instead of using a begin and end element like a traditional container range would.

The advantage is that removing the "end" element (that has nothing to do with the range) won't invalidate the cursor.

What I wanted was the functionality of C++ iterators without the danger that comes with actually iterating them.

-Steve
June 20, 2020
On Saturday, 20 June 2020 at 13:07:41 UTC, Paul Backus wrote:
> On Saturday, 20 June 2020 at 12:42:01 UTC, Stanislav Blinov wrote:
>> On Saturday, 20 June 2020 at 10:43:41 UTC, Paul Backus wrote:
>>
>>> Also, switch from `void popFront()` to `typeof(this) rest`, so that we can have `const` and `immutable` ranges.
>>
>> *Switch* is probably too restrictive. For a given range a `popFront` may be more efficient than a `range = range.rest`. Making `rest` a valid range primitive though, and using where appropriate - that'd be awesome.
>
> For non-forward ranges, there's no promise that the original range (or any copies of it) will remain valid after calling `rest`, so you can always implement `rest` like this:
>
>     auto rest() {
>         this.popFront;
>         return this;
>     }

Can’t make it const then... also some ranges probably cannot bee const by design, e.g. byLine etc.
June 20, 2020
On Saturday, 20 June 2020 at 18:34:37 UTC, Johannes Loher wrote:
> On Saturday, 20 June 2020 at 13:07:41 UTC, Paul Backus wrote:
>>
>> For non-forward ranges, there's no promise that the original range (or any copies of it) will remain valid after calling `rest`, so you can always implement `rest` like this:
>>
>>     auto rest() {
>>         this.popFront;
>>         return this;
>>     }
>
> Can’t make it const then... also some ranges probably cannot bee const by design, e.g. byLine etc.

Yes, ranges with popFront can't be const. The difference is that, with `rest`/`tail` as the default, *some* ranges that are currently forced to use popFront unnecessarily can be made const-compatible.

Ranges that continue to use popFront, for whatever reason (performance, backwards compatibility, etc.) will remain mutable-only.
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.

How does that work? You'd have to use recursion I guess? ugly. Why do we need ranges for something like this?

-Steve
June 20, 2020
On Saturday, 20 June 2020 at 18:26:43 UTC, Steven Schveighoffer wrote:
> On 6/20/20 8:30 AM, Stanislav Blinov wrote:

>> 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.

`find` returns the remainder of the range, i.e it's (pseudocode):

auto find(alias pred,R)(R r)
{
    while (!r.empty && !pred(r.front))
        r.popFront;
    return r;
}

There should be a `move` at that return. There's no NRVO, the compiler makes a copy there.  Which, of course, it wouldn't be able to if R was non-copyable :) It *may* become a move under Walter's proposal:

auto find(alias pred,R)(return R r)
{
    while (!r.empty && !pred(r.front))
        r.popFront;
    return r; // return what's called a `move ref` in the proposal
}

but until then?..

> 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.

We're talking ranges here, not file handles :) Most algorithms in Phobos take ranges by value.

> 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.

*Why* is it bad?
June 20, 2020
On Saturday, 20 June 2020 at 18:26:43 UTC, Steven Schveighoffer wrote:
> 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.

Interesting discussion. Could you expand on this comment? Several people have mentioned this.

I write my own input ranges somewhat regularly. I've never had the need to make them forward ranges. However, the typical reason for creating a range is because I have application specific data that I want to iterate over (and usually construct) lazily. Input ranges are very convenient way to do this. I do end up making many of them reference ranges though.

So, I'm wondering if its really that input-only ranges are rare, or if it's that the number of algorithms that can be used on input-only ranges is small. Or perhaps I'm not quite grokking the distinction between a "true" input-only range and one that satisfies isInputRange, but none of the other range primitive tests.

--Jon
June 20, 2020
On Saturday, 20 June 2020 at 19:23:55 UTC, Jon Degenhardt wrote:
> On Saturday, 20 June 2020 at 18:26:43 UTC, Steven Schveighoffer wrote:
>> 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.
>
> Interesting discussion. Could you expand on this comment? Several people have mentioned this.
>
> I write my own input ranges somewhat regularly. I've never had the need to make them forward ranges. However, the typical reason for creating a range is because I have application specific data that I want to iterate over (and usually construct) lazily. Input ranges are very convenient way to do this. I do end up making many of them reference ranges though.
>
> So, I'm wondering if its really that input-only ranges are rare, or if it's that the number of algorithms that can be used on input-only ranges is small. Or perhaps I'm not quite grokking the distinction between a "true" input-only range and one that satisfies isInputRange, but none of the other range primitive tests.
>
> --Jon

Could you easily make your ranges into forward ranges if you wanted to? That is, if you copy one of them, and then iterate the original, does the copy remain valid? If so, they're not "true" input ranges.

A "true" input range is something like a file handle, where you couldn't implement `save` even if you wanted to--iterating one copy automatically invalidates all the others.
June 20, 2020
On Saturday, 20 June 2020 at 19:35:07 UTC, Paul Backus wrote:
> On Saturday, 20 June 2020 at 19:23:55 UTC, Jon Degenhardt wrote:
>> On Saturday, 20 June 2020 at 18:26:43 UTC, Steven Schveighoffer wrote:
>>> 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.
>>
>> Interesting discussion. Could you expand on this comment? Several people have mentioned this.
>>
>> I write my own input ranges somewhat regularly. I've never had the need to make them forward ranges. However, the typical reason for creating a range is because I have application specific data that I want to iterate over (and usually construct) lazily. Input ranges are very convenient way to do this. I do end up making many of them reference ranges though.
>>
>> So, I'm wondering if its really that input-only ranges are rare, or if it's that the number of algorithms that can be used on input-only ranges is small. Or perhaps I'm not quite grokking the distinction between a "true" input-only range and one that satisfies isInputRange, but none of the other range primitive tests.
>>
>> --Jon
>
> Could you easily make your ranges into forward ranges if you wanted to? That is, if you copy one of them, and then iterate the original, does the copy remain valid? If so, they're not "true" input ranges.
>
> A "true" input range is something like a file handle, where you couldn't implement `save` even if you wanted to--iterating one copy automatically invalidates all the others.

Ah, thanks for this clarification. I'd have to go back and look at all the different ranges I've written, but I'd say there are a fair number in both camps. Now I'm wondering if there's a correlation with the use of reference ranges. That is, have I used reference ranges when the range falls into the "true" input range set. The answer may be yes.

June 20, 2020
On 6/20/20 3:16 PM, Stanislav Blinov wrote:
> On Saturday, 20 June 2020 at 18:26:43 UTC, Steven Schveighoffer wrote:
>> On 6/20/20 8:30 AM, Stanislav Blinov wrote:
> 
>>> 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.
> 
> `find` returns the remainder of the range, i.e it's (pseudocode):
> 
> auto find(alias pred,R)(R r)
> {
>      while (!r.empty && !pred(r.front))
>          r.popFront;
>      return r;
> }
> 
> There should be a `move` at that return. There's no NRVO, the compiler makes a copy there.  Which, of course, it wouldn't be able to if R was non-copyable :)

Yeah, it needs to use move. But the usage doesn't change, which is what I thought you were referring to.

> It *may* become a move under Walter's proposal:
> 
> auto find(alias pred,R)(return R r)
> {
>      while (!r.empty && !pred(r.front))
>          r.popFront;
>      return r; // return what's called a `move ref` in the proposal
> }
> 
> but until then?..

You'd have to modify Phobos.

> 
>> 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.
> 
> We're talking ranges here, not file handles :) Most algorithms in Phobos take ranges by value.

Which means that you have to destroy the original, because it's no longer valid.

Today, you do:

auto otherRange = inputRange.find(something);

use(inputRange); // invalid, but compiler lets you. And it probably messes up otherRange too.

> 
>> 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.
> 
> *Why* is it bad?

Because there's no enforcement. You can copy most forward ranges without using save, and it works EXACTLY the same. In fact, most save implementations are {return this;}. If you ever use a forward range that requires the use of .save to actually copy it, you will have problems.

So it essentially promotes generic code that is incorrect, but works fine for most cases. I'm positive there are still cases like this in Phobos. It's very easy and effortless to make a copy without using .save.

When's the last time you saw:

foreach(x; someLvalueRange.save)

Because if you don't see that, for a forward range, it's an incorrect usage.

-Steve
June 20, 2020
On 6/14/2020 9:18 AM, Avrina wrote:
> Size doesn't matter if it doesn't work.

I use it all the time, it works fine. It does lack the features that make makefiles incomprehensible, though.