June 21, 2020
On 6/21/20 6:06 PM, Timon Gehr wrote:
> On 20.06.20 16:51, Andrei Alexandrescu wrote:
>>
>> 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.
> 
> What if the caller does not know how to construct a T?
> 
> Nullable!T fetchNext();
> 
> (I get that D's support for algebraic data types is subpar, but maybe that is something to look into.)

That also creates a copy for each element, which may be disadvantageous for certain range types. (Also does not give access to references to the original elements, for you can't say Nullable!(ref T).)

Making an API that is simple and works with in-memory elements as well as values synthesized on the fly is quite a challenge.
June 22, 2020
On 6/20/20 8:12 PM, Andrei Alexandrescu wrote:
> On 6/20/20 3:03 PM, Steven Schveighoffer wrote:
>> 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.
>>>>
>>>>
>>>
>>> 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?
> 
> Think Hakell lists. They can implement tail easily (just return the next pointer) but can't define popFront.
> 

My question wasn't about how such a thing could be implemented, but how it works with const ranges.

foreach(x; someConstRange) I think wouldn't be possible. I think you'd have to recurse:

void process(const Range r)
{
   subProcess(r.front);
   process(r.rest);
}

The point is to question the statement "so that we can have `const` and `immutable` ranges".

Sure, we could implement recursive versions of find, etc. I don't know if that's worth it.

-Steve
June 22, 2020
On Monday, 22 June 2020 at 12:15:39 UTC, Steven Schveighoffer wrote:
> On 6/20/20 8:12 PM, Andrei Alexandrescu wrote:
>> On 6/20/20 3:03 PM, Steven Schveighoffer wrote:
>>> 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.
>>>>>
>>>>>
>>>>
>>>> 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?
>> 
>> Think Hakell lists. They can implement tail easily (just return the next pointer) but can't define popFront.
>> 
>
> My question wasn't about how such a thing could be implemented, but how it works with const ranges.
>
> foreach(x; someConstRange) I think wouldn't be possible. I think you'd have to recurse:
>
> void process(const Range r)
> {
>    subProcess(r.front);
>    process(r.rest);
> }
>

I think maybe what is needed is a notion of "head-mutable"?

void process(const headmut Range r)
{
  while (!r.empty) {
    // subProcess still has to take an immmutable r. this does not expose mutable state.
    // headmut converts to immutable on pass-by-value because it makes a copy.
    subProcess(r.front);
    r = r.rest; // can be assigned because headmut, meaning references are still immutable
  }
}

I don't know how this would interact with dip1000. Maybe headmut could convert as immutable but expression scoped, which would come down to the same thing.

Something like this is also probably the right backing type to use for many containers, like Nullable.
June 22, 2020
On Monday, 22 June 2020 at 12:15:39 UTC, Steven Schveighoffer wrote:
>
> My question wasn't about how such a thing could be implemented, but how it works with const ranges.
>
> foreach(x; someConstRange) I think wouldn't be possible. I think you'd have to recurse:
>
> void process(const Range r)
> {
>    subProcess(r.front);
>    process(r.rest);
> }
>
> The point is to question the statement "so that we can have `const` and `immutable` ranges".
>
> Sure, we could implement recursive versions of find, etc. I don't know if that's worth it.
>
> -Steve

Well, currently, range algorithms can't work with const ranges *at all*, recursively or iteratively. So from a user perspective, this would be a strict improvement on the status quo.
June 22, 2020
On 6/22/20 10:20 AM, Paul Backus wrote:
> On Monday, 22 June 2020 at 12:15:39 UTC, Steven Schveighoffer wrote:
>>
>> My question wasn't about how such a thing could be implemented, but how it works with const ranges.
>>
>> foreach(x; someConstRange) I think wouldn't be possible. I think you'd have to recurse:
>>
>> void process(const Range r)
>> {
>>    subProcess(r.front);
>>    process(r.rest);
>> }
>>
>> The point is to question the statement "so that we can have `const` and `immutable` ranges".
>>
>> Sure, we could implement recursive versions of find, etc. I don't know if that's worth it.
>>
> 
> Well, currently, range algorithms can't work with const ranges *at all*, recursively or iteratively. So from a user perspective, this would be a strict improvement on the status quo.

Algorithms can work with const ranges -- as long as the range is an array:

const int[] arr = [1, 2, 3, 4, 5];

auto x = arr.find(3);

assert(x == [3, 4, 5]);

I think the better option is to focus on making it possible to duplicate this possibility for generic ranges rather than implement a new and awkward API.

FeepingCreature is right, we should try and create a head mutable mechanism. I have envisioned it in the past as a tail-modifier mechanism (e.g. tail-const).

-Steve
June 22, 2020
On Monday, 22 June 2020 at 14:33:53 UTC, Steven Schveighoffer wrote:
> On 6/22/20 10:20 AM, Paul Backus wrote:
>> 
>> Well, currently, range algorithms can't work with const ranges *at all*, recursively or iteratively. So from a user perspective, this would be a strict improvement on the status quo.
>
> Algorithms can work with const ranges -- as long as the range is an array:
>
> const int[] arr = [1, 2, 3, 4, 5];
>
> auto x = arr.find(3);
>
> assert(x == [3, 4, 5]);
>
> I think the better option is to focus on making it possible to duplicate this possibility for generic ranges rather than implement a new and awkward API.
>
> FeepingCreature is right, we should try and create a head mutable mechanism. I have envisioned it in the past as a tail-modifier mechanism (e.g. tail-const).
>
> -Steve

This isn't really "algorithms working with const ranges"; rather, it's "const(T[]) implicitly converts to const(T)[]". The algorithm itself never sees a const range.

I suppose we could have the same thing for user-defined types if we added `@implicit opCast`, or a more restrictive version like `opHeadMutable`, but the whole thing seems backwards to me. const(T[]) converting to const(T)[] is a special case in the language introduced to compensate for the shortcomings `popFront`. Rather than doubling down on it, why not fix the issue at its source?
June 22, 2020
On Monday, 22 June 2020 at 14:33:53 UTC, Steven Schveighoffer wrote:
> [snip]
>
> FeepingCreature is right, we should try and create a head mutable mechanism. I have envisioned it in the past as a tail-modifier mechanism (e.g. tail-const).
>
> -Steve

I just read a thread from 2010 on tail const/head mutable. Kind of amazing that it is a difficult enough problem that it still hasn't been resolved.
June 22, 2020
On Monday, 22 June 2020 at 12:28:06 UTC, FeepingCreature wrote:
>
> I think maybe what is needed is a notion of "head-mutable"?

It's called Final and already in Phobos:

https://dlang.org/phobos/std_experimental_typecons.html#.Final

Though it comes with a few bugs like e.g. [1] or [2].

[1] https://issues.dlang.org/show_bug.cgi?id=17272
[2] https://github.com/dlang/phobos/pull/6596
June 22, 2020
On Monday, 22 June 2020 at 15:29:25 UTC, Seb wrote:
> On Monday, 22 June 2020 at 12:28:06 UTC, FeepingCreature wrote:
>>
>> I think maybe what is needed is a notion of "head-mutable"?
>
> It's called Final and already in Phobos:
>
> https://dlang.org/phobos/std_experimental_typecons.html#.Final
>
> Though it comes with a few bugs like e.g. [1] or [2].
>
> [1] https://issues.dlang.org/show_bug.cgi?id=17272
> [2] https://github.com/dlang/phobos/pull/6596

Sorry. I read to quick. Final is head-const and tail-mutable.
Rebindable is the Phobos solution to head-mutable / tail-const:

https://dlang.org/phobos/std_typecons.html#Rebindable

The biggest gotcha of Rebindable is that it doesn't work with structs [1] which vastly limits its usability.

[1] https://github.com/dlang/phobos/pull/6136
June 22, 2020
On 6/22/20 10:55 AM, Paul Backus wrote:
> On Monday, 22 June 2020 at 14:33:53 UTC, Steven Schveighoffer wrote:
>> On 6/22/20 10:20 AM, Paul Backus wrote:
>>>
>>> Well, currently, range algorithms can't work with const ranges *at all*, recursively or iteratively. So from a user perspective, this would be a strict improvement on the status quo.
>>
>> Algorithms can work with const ranges -- as long as the range is an array:
>>
>> const int[] arr = [1, 2, 3, 4, 5];
>>
>> auto x = arr.find(3);
>>
>> assert(x == [3, 4, 5]);
>>
>> I think the better option is to focus on making it possible to duplicate this possibility for generic ranges rather than implement a new and awkward API.
>>
>> FeepingCreature is right, we should try and create a head mutable mechanism. I have envisioned it in the past as a tail-modifier mechanism (e.g. tail-const).
>>
> 
> This isn't really "algorithms working with const ranges"; rather, it's "const(T[]) implicitly converts to const(T)[]". The algorithm itself never sees a const range.

I don't see a difference. When you copy a range as a parameter, the head is a different piece of memory. This is why it works. Why is it important how it works?

> I suppose we could have the same thing for user-defined types if we added `@implicit opCast`, or a more restrictive version like `opHeadMutable`, but the whole thing seems backwards to me. const(T[]) converting to const(T)[] is a special case in the language introduced to compensate for the shortcomings `popFront`. Rather than doubling down on it, why not fix the issue at its source?

That isn't why tail-const was "introduced". And I would consider enabling this feature for custom types fixing it at the source.

Why does it seem backwards to you?

Perhaps you are thinking of how templates automatically strip the head const? That I don't necessarily agree with either, but there isn't a good way to say "take this parameter as head-mutable" in generic code, which is why *that* was added. If we did have a mechanism to say that (such as FeepingCreture's `headmut` example), then we wouldn't need that special treatment.

-Steve