www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Plan for InputRange?

reply Dukc <ajieskola gmail.com> writes:
Yesterday, I came across [this 
bug](https://issues.dlang.org/show_bug.cgi?id=19544) that is 
caused by design of std.range.interfaces. The problem is that 
`InputRange` has an abstract member function `moveFront()`.

Why is this a problem, you could ask - if I'm able to get a 
`front` of a range by copy, surely I can get it when I don't even 
require a copy? This is all good and true with most types. But 
`moveFront()` from `std.range.primitives`, just like 
`core.lifetime.move`, will require the element to be a LValue. 
`moveFront()` is smart enough to use copy with RValues if there 
are no copy constructors or postblits, but with them a reference 
access to `front` is required. It follows that the dynamic range 
interfaces will not work at for ranges with RValue elements with 
copy constructors:

```D
import std;

struct HasCC
{   inout this(ref inout typeof(this)){}
}

void main()
{   // will not compile
     auto dynamicRange = repeat(HasCC.init).inputRangeObject;
}
```

What should we do about this? One could argue that `moveFront` 
belongs to `InputAssignable` as opposed to `InputRange`, but I 
think it would be too much breakage to change it like that 
anymore.

Another option might be to say that `moveFront` should behave 
just like `front` when `front` is a RValue. Either just in 
`std.range.interfaces` or also in `std.range.primitives`. Perhaps 
even `move` could behave like that? But maybe there is some 
reason I'm missing why they do not act like that already?

We could also leave it just as is, documenting the limitation in 
`std.range.interfaces`. It would not necessarily mean a permanent 
defeat, because of possibility to fix it in Phobos V2 (Timeline 
estimates on that, anyone?).
Sep 28 2021
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 28 September 2021 at 15:07:59 UTC, Dukc wrote:
 What should we do about this? One could argue that `moveFront` 
 belongs to `InputAssignable` as opposed to `InputRange`, but I 
 think it would be too much breakage to change it like that 
 anymore.
Maybe `moveFront` could throw an exception at runtime if the wrapped range does not support it. Something like the following: ```d override E moveFront() { static if (__traits(compiles, r.moveFront)) return r.moveFront; else throw new Exception("Cannot move the front of a `" ~ typeof(r).stringof ~ "`"); } ``` It's a stupid hack, but that may be the best way to work around a stupid design mistake like this.
Sep 28 2021
parent reply bauss <jj_1337 live.dk> writes:
On Wednesday, 29 September 2021 at 02:06:16 UTC, Paul Backus 
wrote:
 On Tuesday, 28 September 2021 at 15:07:59 UTC, Dukc wrote:
 What should we do about this? One could argue that `moveFront` 
 belongs to `InputAssignable` as opposed to `InputRange`, but I 
 think it would be too much breakage to change it like that 
 anymore.
Maybe `moveFront` could throw an exception at runtime if the wrapped range does not support it. Something like the following: ```d override E moveFront() { static if (__traits(compiles, r.moveFront)) return r.moveFront; else throw new Exception("Cannot move the front of a `" ~ typeof(r).stringof ~ "`"); } ``` It's a stupid hack, but that may be the best way to work around a stupid design mistake like this.
Why should it throw an exception when you can detect it at compiletime? ``` override E moveFront() { static if (__traits(compiles, r.moveFront)) return r.moveFront; else static assert(0, "Cannot move the front of a `" ~ typeof(r).stringof ~ "`"); } ```
Sep 28 2021
parent reply Dukc <ajieskola gmail.com> writes:
On Wednesday, 29 September 2021 at 06:10:30 UTC, bauss wrote:
 Why should it throw an exception when you can detect it at 
 compiletime?

 ```
 override E moveFront()
 {
     static if (__traits(compiles, r.moveFront))
         return r.moveFront;
     else
         static assert(0, "Cannot move the front of a `" ~ 
 typeof(r).stringof ~ "`");
 }
 ```
`inputRangeObject` already does something like that, but there lies the problem. It means that you cannot wrap a range with immobile elements in any of the `std.range.primitives` at all, even if you never move them. Perhaps it could do a regular `assert(false)` though. I'm not sure it should throw an exception, since calling `moveFront` where one is not implemented is a bug, not an environmental error. On the other hand catchable exceptions are the simplest way to let the user to introspect whether the elements are mobile.
Sep 29 2021
parent Paul Backus <snarwin gmail.com> writes:
On Wednesday, 29 September 2021 at 08:20:31 UTC, Dukc wrote:
 Perhaps it could do a regular `assert(false)` though. I'm not 
 sure it should throw an exception, since calling `moveFront` 
 where one is not implemented is a bug, not an environmental 
 error. On the other hand catchable exceptions are the simplest 
 way to let the user to introspect whether the elements are 
 mobile.
When you use `assert`, you put the responsibility on the calling function to avoid the failure case (e.g., by calling `empty` before calling `front`). In this case, though, because `InputRange` hides the wrapped range's original type, there is nothing the caller can do in advance to check whether `moveFront` will fail. So it has to be `moveFront`'s responsibility to detect failure, which means it must use a recoverable exception.
Sep 29 2021