digitalmars.D - Plan for InputRange?
- Dukc (37/37) Sep 28 2021 Yesterday, I came across [this
- Paul Backus (15/19) Sep 28 2021 Maybe `moveFront` could throw an exception at runtime if the
- bauss (14/33) Sep 28 2021 Why should it throw an exception when you can detect it at
- Dukc (10/22) Sep 29 2021 `inputRangeObject` already does something like that, but there
- Paul Backus (8/14) Sep 29 2021 When you use `assert`, you put the responsibility on the calling
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
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
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: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 ~ "`"); } ```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
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
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