March 26, 2021
On Wednesday, 24 March 2021 at 19:23:21 UTC, Per Nordlöw wrote:
> What's the motive behinds D's range design choice of needing
>
>     if (!empty)
>     {
>         // use front or back
>     }
>
> instead of having front returning an optional/maybe type with enforced pattern matching?
>
> Lack of builtin Optional type?
>
> Choosing the Optional path would have avoided the need for putting error diagnostics such as
>
> https://github.com/dlang/phobos/commit/9bd2f2ba8ff1124a044560c4e6912a13cb5ac694
>
> in the standard library of such an alternative solution.

Actually changing front() to return optionals would probably break many things. However, it could be handy to add a method called nullableFront or frontOrNull (and nullableBack or backOrNull for bidirectional ranges) to std.range.
March 26, 2021
On Friday, 26 March 2021 at 08:49:30 UTC, Piotr Mitana wrote:
> Actually changing front() to return optionals would probably break many things. However, it could be handy to add a method called nullableFront or frontOrNull (and nullableBack or backOrNull for bidirectional ranges) to std.range.

Agreed. Alternative namings could be maybeFront or optionalFront.
March 26, 2021
On Friday, 26 March 2021 at 09:10:28 UTC, Per Nordlöw wrote:
> On Friday, 26 March 2021 at 08:49:30 UTC, Piotr Mitana wrote:
>> Actually changing front() to return optionals would probably break many things. However, it could be handy to add a method called nullableFront or frontOrNull (and nullableBack or backOrNull for bidirectional ranges) to std.range.
>
> Agreed. Alternative namings could be maybeFront or optionalFront.

A D specific solution could be to add compiler diagnostics that checks whether a call to `x.front` and x.popFront() is guarded by an `if (!x.empty)`. Provided no mutation has happened since the call to !empty and `x` is not accessible via other threads.
March 26, 2021
On Wednesday, 24 March 2021 at 19:23:21 UTC, Per Nordlöw wrote:
> What's the motive behinds D's range design choice of needing
>
>     if (!empty)
>     {
>         // use front or back
>     }
>
> instead of having front returning an optional/maybe type with enforced pattern matching?
>
> Lack of builtin Optional type?
>
> Choosing the Optional path would have avoided the need for putting error diagnostics such as
>
> https://github.com/dlang/phobos/commit/9bd2f2ba8ff1124a044560c4e6912a13cb5ac694
>
> in the standard library of such an alternative solution.

I think one reason is that unlike Rust, D doesn't have a safe way to return Optional!(ref T) so we need front and empty so Ranges can return the items by reference.
March 26, 2021
On Friday, 26 March 2021 at 13:38:01 UTC, vitoroak wrote:
> I think one reason is that unlike Rust, D doesn't have a safe way to return Optional!(ref T) so we need front and empty so Ranges can return the items by reference.

Ahh, thanks.
March 26, 2021
On Friday, 26 March 2021 at 13:23:25 UTC, Per Nordlöw wrote:
> A D specific solution could be to add compiler diagnostics that checks whether a call to `x.front` and x.popFront() is guarded by an `if (!x.empty)`. Provided no mutation has happened since the call to !empty and `x` is not accessible via other threads.

// in @safe code
scope p = &r;
if (!r.empty) {
  p.popFront;
  r.front.writeln; // oops
}

I don't think it's practical for the compiler to detect more complicated variants of the above. The beauty of languages with compile-time checked optional types is that the value has to exist in order to unwrap the optional.
March 26, 2021
On Friday, 26 March 2021 at 13:38:01 UTC, vitoroak wrote:
>
> I think one reason is that unlike Rust, D doesn't have a safe way to return Optional!(ref T) so we need front and empty so Ranges can return the items by reference.

I believe with DIP 1000 it should be possible to return something like `Optional!(Ref!T)`, where `Ref!T` is a safe wrapper around a `T*`.

Of course the ideal would be to make `ref` itself into a type qualifier (`ref(T)`).
March 26, 2021
On Friday, 26 March 2021 at 15:02:27 UTC, Nick Treleaven wrote:
> compile-time checked optional types is that the value has to exist in order to unwrap the optional.

Can you elaborate on what you mean by "compile-time checked optional types"?
March 26, 2021
On 3/24/21 3:23 PM, Per Nordlöw wrote:
> What's the motive behinds D's range design choice of needing
> 
>      if (!empty)
>      {
>          // use front or back
>      }
> 
> instead of having front returning an optional/maybe type with enforced pattern matching?
> 
> Lack of builtin Optional type?
> 
> Choosing the Optional path would have avoided the need for putting error diagnostics such as
> 
> https://github.com/dlang/phobos/commit/9bd2f2ba8ff1124a044560c4e6912a13cb5ac694 
> 
> 
> in the standard library of such an alternative solution.

I would say separation of concerns. Consider:

int someExpensiveCalc(int);

auto r = someArr.map!someExpensiveCalc;

r.empty is simply a forward to someArr.empty. However, r.front calls someExpensiveCalc (if not empty).

So code that looks specifically at emptiness might not want to trigger the expensive call unnecessarily. e.g. canFind.

Since empty is a distinct abstraction, it's the simplest building block. You can actually MAKE a "range-like" thing that uses empty to produce what you want. It's problematic the other way around.

That being said, there is definitely a case for straight-input-ranges to have a different API (maybe one without empty), since iterating a non-forward range can possibly destroy it, and if you change the API to simply an Optional!T next(), then you don't need to cache the element (currently I think the biggest wart in the range system).

But such a thing would have to possibly be supported in the language (ranges are a language feature in that they hook to foreach, I would expect the same for such input ranges).

Regarding the question about the github commit, that's an entirely different thing. Notice that it's an assert, which means it can be removed in correctly-written programs. If you return an Optional, emptiness MUST be checked on every access to an element, even when you know (or have proven) it's not empty.

I would support a new kind of range that uses `Optional!T next()` as its API. I would not support `Optional!T front(); void popFront()`.

-Steve
March 26, 2021
On Friday, 26 March 2021 at 15:53:39 UTC, Steven Schveighoffer wrote:
> ...
> I would support a new kind of range that uses `Optional!T next()` as its API. I would not support `Optional!T front(); void popFront()`.

Interesting.

For reference, note that Rust also has

https://doc.rust-lang.org/std/iter/struct.Peekable.html

alongside

https://doc.rust-lang.org/core/iter/trait.Iterator.html