November 02, 2021
On Tuesday, 2 November 2021 at 18:09:55 UTC, H. S. Teoh wrote:
> Why is this necessary?  I thought we're getting rid of std.range.interfaces.

It is actually really, really, useful. If phobos didn't offer it, someone would reinvent it anyway.

(In fact, there's a lot of cases where using them is more efficient than generating more and more code...)
November 02, 2021
On Tue, Nov 02, 2021 at 07:32:56PM +0000, Adam D Ruppe via Digitalmars-d wrote:
> On Tuesday, 2 November 2021 at 18:09:55 UTC, H. S. Teoh wrote:
> > Why is this necessary?  I thought we're getting rid of std.range.interfaces.
> 
> It is actually really, really, useful. If phobos didn't offer it, someone would reinvent it anyway.
> 
> (In fact, there's a lot of cases where using them is more efficient than generating more and more code...)

I find it very useful as well, but according to Andrei, v2 will get rid of class-based ranges, and if those are wanted we should use a struct wrapper instead.

In any case, if struct wrappers are the way to go, then we better have a standard way of constructing them.  It just won't be the same thing as std.range.interfaces.


T

-- 
They say that "guns don't kill people, people kill people." Well I think the gun helps. If you just stood there and yelled BANG, I don't think you'd kill too many people. -- Eddie Izzard, Dressed to Kill
November 02, 2021
On Tuesday, 2 November 2021 at 19:44:02 UTC, H. S. Teoh wrote:
> I find it very useful as well, but according to Andrei, v2 will get rid of class-based ranges, and if those are wanted we should use a struct wrapper instead.

Well, you'd keep the class for the actual wrapper, then the struct is just a thin thing on top of that.

interface IInputRange {
   // yada
}

struct InputRange {
   IInputRange c;
}

So you'd wrap it like that. Then for forward range make the copy constructor call the clone/save/deepCopy/whatever method on the class.

Then the std.algorithm things just take InputRange. A few details to work out but it is a simple enough thing to do.

November 02, 2021
On 2021-11-02 15:32, Adam D Ruppe wrote:
> On Tuesday, 2 November 2021 at 18:09:55 UTC, H. S. Teoh wrote:
>> Why is this necessary?  I thought we're getting rid of std.range.interfaces.
> 
> It is actually really, really, useful. If phobos didn't offer it, someone would reinvent it anyway.
> 
> (In fact, there's a lot of cases where using them is more efficient than generating more and more code...)

Yah, polymorphism has its place. The only problem is passing around reference ranges. They should have a thin struct wrapper that carries the proper copy semantics.
November 02, 2021

On Tuesday, 2 November 2021 at 18:09:55 UTC, H. S. Teoh wrote:

>

On Tue, Nov 02, 2021 at 12:11:24PM +0000, Dukc via Digitalmars-d wrote:

>

On Tuesday, 2 November 2021 at 02:45:11 UTC, Andrei Alexandrescu wrote:

>

Exactly. No need to support class ranges - simple wrappers can do everything class-like indirection does. Thanks.

Trying to write up a plan based on that one, so you can correct and/or spot weaknesses

  • stuff in std.v2.range.interfaces and
    std.v2.concurrency.Generator will continue to be ranges from
    Phobos v1 viewpoint but not from Phobos v2 viewpoint.

Why is this necessary? I thought we're getting rid of std.range.interfaces.

I quess we could write a more advanced alternative, but I'd prefer to keep the range interfaces around until someone does, to avoid scope creep.

The downside is going to be that Phobos v2 cannot use the interfaces directly as they aren't ranges anymore, but that's what the valueRange and the "other function" I mentioned are for.

>

What's a value range?

Opposite of a reference range - copying implies save().

>

Interesting idea. So basically a shim for easy translation of v1-based code to v2-based code? That would be nice for gradual migration. It would have to exclude certain incompatible things like autodecoded strings, though. Otherwise it will result in a mess.

I propose that the shim will autodecode if imported from v1 (if we even add it to v1) but not if imported from v2 - just like the rest of the range-accepting functions.

> >
  • Phobos v2 ranges should still continue to provide the save method
    so they can be passed to v1 ranges.
    [...]

I'm not sure this is such a good idea, because v2 ranges may have fundamental incompatibilities with v1 algorithms, e.g., a v2 string range (non-autodecoded) being passed to a v1 algorithm (autodecoded) will probably produce the wrong results, likely silently, which is bad.

I agree that it's better to avoid function chains like that if easily possible. But the underlying rule is simple and unambiguous: a Phobos v1 function will autodecode a character array, a v2 function will not. If the character range is anything other than a plain array, they behave identically: the decoding or lack of thereof depends on the range itself.

Not worth to make the interoperability difficult just because of that IMO. If the users start having problems, they can voluntarily avoid mixing the character-handling v1 and v2 ranges - they still enjoy easier interoperability with other ranges.

November 02, 2021
On Tue, Nov 02, 2021 at 04:17:06PM -0400, Andrei Alexandrescu via Digitalmars-d wrote:
> On 2021-11-02 15:32, Adam D Ruppe wrote:
> > On Tuesday, 2 November 2021 at 18:09:55 UTC, H. S. Teoh wrote:
> > > Why is this necessary?  I thought we're getting rid of std.range.interfaces.
> > 
> > It is actually really, really, useful. If phobos didn't offer it, someone would reinvent it anyway.
> > 
> > (In fact, there's a lot of cases where using them is more efficient than generating more and more code...)
> 
> Yah, polymorphism has its place. The only problem is passing around reference ranges. They should have a thin struct wrapper that carries the proper copy semantics.

Yes, so we need a standard way of constructing such wrappers.  Possibly an addition to stdv2.range.interfaces?  Or maybe just have the wrapper constructors return the constructed polymorphic range pre-(shrink)wrapped. :-)


T

-- 
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird. -- D. Knuth
November 02, 2021
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).
November 02, 2021
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.
November 02, 2021
On Tue, Nov 02, 2021 at 11:07:08PM +0000, Paul Backus via Digitalmars-d wrote:
> On Tuesday, 2 November 2021 at 21:58:20 UTC, Andrei Alexandrescu wrote:
[...]
> > 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.

The problem with manually-added tags of this sort is that people forget to do it, and that leads to trouble.  Preferably, it should be something already implicit in the range type itself, that does not require additional effort to tag.

I'm kinda toying with the idea of struct == forward range, class == input range: the difference is inherent in the type itself and requires no further effort beyond the decision to use a by-value type vs. a by-reference type, which coincides with the decision to make something an input range or a forward range.


T

-- 
Political correctness: socially-sanctioned hypocrisy.
November 03, 2021

On Wednesday, 3 November 2021 at 00:01:33 UTC, H. S. Teoh wrote:

>

The problem with manually-added tags of this sort is that people forget to do it, and that leads to trouble. Preferably, it should be something already implicit in the range type itself, that does not require additional effort to tag.

I'm kinda toying with the idea of struct == forward range, class == input range: the difference is inherent in the type itself and requires no further effort beyond the decision to use a by-value type vs. a by-reference type, which coincides with the decision to make something an input range or a forward range.

Having input ranges implement next and forward ranges implement head and tail would also make them easy to distinguish.