digitalmars.D - Wanted: best way to express default expectations
- Dukc (52/52) Oct 10 2019 This is a very common pattern:
- Nicholas Wilson (2/17) Oct 10 2019 https://dlang.org/phobos/std_algorithm_iteration.html#.filter
- Dukc (4/5) Oct 10 2019 For loops, that's an excellent choice. But what about early
- Dukc (5/11) Oct 10 2019 And also, sometimes it's not practical to filter the range up
- Petar Kirov [ZombineDev] (10/22) Oct 10 2019 https://dlang.org/phobos/std_algorithm_iteration.html#.each
- Dukc (12/21) Oct 10 2019 These make possible to emulate `break` for the innermost range,
- Jesse Phillips (13/25) Oct 12 2019 Ignore the result has no meaning
- Jesse Phillips (19/33) Oct 12 2019 Sorry forgot the foreach break part.
- Dukc (9/26) Oct 14 2019 This is not exactly what I wanted, but probably the best
- Dukc (9/26) Oct 14 2019 This is not exactly what I wanted, but probably the best
- Dennis (8/11) Oct 14 2019 I think a new keyword for this has very little chance.
- Dukc (5/12) Oct 16 2019 Would also do the trick imo, and also avoid a new keyword. Good
- Paul Backus (8/35) Oct 14 2019 Pretty sure you don't need the foreach loop in this example; you
- Jesse Phillips (5/35) Oct 14 2019 How is not what you want? I realize it doesn't use the same
- Dukc (10/18) Oct 16 2019 I think you nailed it - there is apparently no (better)
This is a very common pattern: ``` auto sumSquares(R)(R values) if (isInputRange!R && isPointer!(ElementType!R) && isIntegral!(typeof(*values.front))) { typeof(*values.front) result = 0; foreach (value; values) { if (value is null) continue; result += *value * *value; } return result; } ``` `values` may obviously contain null values, which have to be taken account in the body of the `foreach` loop. The problem here is that `if (value is null)` is stating the condition rest of the code block does NOT want. This is exactly the opposite behaviour of `assert`, `enforce` and `in` statements, and will make - I think - maintenance-induced bugs more likely. You can't just add a library solution in conventional D style, because a lambda you would pass to `alias` template parameter could not `continue` the foreach loop (nor `return`, `break` or `goto`). a '!' mark to negate the `if` condition works, but at least for my eye, it would be more cognitive load to read such a condition, especially if it has and/or statements that have to be negated as whole. In this case, the best solution would doubtlessly be this: ``` foreach (value; values) if (value !is null) { result += *value * *value; } ``` ...however this is a bad general solution. Often, you have many expectations that must be stated at different parts of the code block. This solution to those cases would mean very deep nesting, and a lot of short `else` statements at the end of the function. Not ideal. Perhaps the best general solution I can come up with is `if (value !is null){} else continue`. It states the default expectation instead of it's negation value, and handles the exceptional case right away. Still, quickly looking, it looks a lot like of an `if` statement that is meant to branch the code block, not just terminate it early, and thus i think the negation (`{} else`) might be missed more easily than with e.g. `assert`s. Note that this does not only apply to loops. It's just as common to shortcut-return from a function in cases where rest of the logic is needless. What do you think? Is there a good possiblility to do some sort of library solution? Or would a DIP be more appropriate? Or is my taste just a bit strange here?
Oct 10 2019
On Thursday, 10 October 2019 at 11:34:44 UTC, Dukc wrote:This is a very common pattern: ``` auto sumSquares(R)(R values) if (isInputRange!R && isPointer!(ElementType!R) && isIntegral!(typeof(*values.front))) { typeof(*values.front) result = 0; foreach (value; values) { if (value is null) continue; result += *value * *value; } return result; } ``` What do you think? Is there a good possiblility to do some sort of library solution?https://dlang.org/phobos/std_algorithm_iteration.html#.filter
Oct 10 2019
On Thursday, 10 October 2019 at 11:44:58 UTC, Nicholas Wilson wrote:https://dlang.org/phobos/std_algorithm_iteration.html#.filterFor loops, that's an excellent choice. But what about early function returns?
Oct 10 2019
On Thursday, 10 October 2019 at 11:47:21 UTC, Dukc wrote:On Thursday, 10 October 2019 at 11:44:58 UTC, Nicholas Wilson wrote:And also, sometimes it's not practical to filter the range up front. You may also want to shortcut out in middle of the `foreach` body, or `break` out of an outer loop, or `return` instead of just aborting the loop.https://dlang.org/phobos/std_algorithm_iteration.html#.filterFor loops, that's an excellent choice. But what about early function returns?
Oct 10 2019
On Thursday, 10 October 2019 at 11:50:59 UTC, Dukc wrote:On Thursday, 10 October 2019 at 11:47:21 UTC, Dukc wrote:https://dlang.org/phobos/std_algorithm_iteration.html#.each allows early stopping by having the iteration function return No.each to stop the iteration, but I'm not sure whether this is the thing you're looking for. If you're trying to solve a more general problem that would require the ability to return early from a nested scope I would also suggest checking opApply (may work for a simpler scenario) and Fibers, which when applied well can provide a substantial increase in expressive power.On Thursday, 10 October 2019 at 11:44:58 UTC, Nicholas Wilson wrote:And also, sometimes it's not practical to filter the range up front. You may also want to shortcut out in middle of the `foreach` body, or `break` out of an outer loop, or `return` instead of just aborting the loop.https://dlang.org/phobos/std_algorithm_iteration.html#.filterFor loops, that's an excellent choice. But what about early function returns?
Oct 10 2019
On Thursday, 10 October 2019 at 13:36:05 UTC, Petar Kirov [ZombineDev] wrote:https://dlang.org/phobos/std_algorithm_iteration.html#.each allows early stopping by having the iteration function return No.each to stop the iteration, but I'm not sure whether this is the thing you're looking for. If you're trying to solve a more general problem that would require the ability to return early from a nested scope I would also suggest checking opApply (may work for a simpler scenario) and Fibers, which when applied well can provide a substantial increase in expressive power.These make possible to emulate `break` for the innermost range, but still they only with loops (not try blocks or the whole function body for example). And even in case of ranges, they don't let one to `return`, `goto` or `break`/`continue` an outer loop directly from the loop body. And no, I'm not looking for a way to do it -an `if` statement can do that like the first example shows. More like, i'm thinking an `if` equivalent that'll execute the statement when NOT true. Basically it would be like `assert`, except it would exit a code block instead of terminating the program when it fails.
Oct 10 2019
On Thursday, 10 October 2019 at 11:50:59 UTC, Dukc wrote:On Thursday, 10 October 2019 at 11:47:21 UTC, Dukc wrote:Ignore the result has no meaning import std.stdio; void main() { import std.algorithm; import std.range; iota(10) .filter!(x => x%2) .until!(x => x>5) .map!"a * a" .filter!(x => x%4).writeln; }On Thursday, 10 October 2019 at 11:44:58 UTC, Nicholas Wilson wrote:And also, sometimes it's not practical to filter the range up front. You may also want to shortcut out in middle of the `foreach` body, or `break` out of an outer loop, or `return` instead of just aborting the loop.https://dlang.org/phobos/std_algorithm_iteration.html#.filterFor loops, that's an excellent choice. But what about early function returns?
Oct 12 2019
On Saturday, 12 October 2019 at 16:24:01 UTC, Jesse Phillips wrote:On Thursday, 10 October 2019 at 11:50:59 UTC, Dukc wrote:Sorry forgot the foreach break part. import std.stdio; void main() { import std.algorithm; import std.range; foreach(i; iota(10) .filter!(x => x%2) .until!(x => x>5) .map!"a * a" .filter!(x => x%4)) { i.writeln; break; } } Note this is all done lazily so there is no walking of the iota which happens.On Thursday, 10 October 2019 at 11:47:21 UTC, Dukc wrote:On Thursday, 10 October 2019 at 11:44:58 UTC, Nicholas Wilson wrote:And also, sometimes it's not practical to filter the range up front. You may also want to shortcut out in middle of the `foreach` body, or `break` out of an outer loop, or `return` instead of just aborting the loop.https://dlang.org/phobos/std_algorithm_iteration.html#.filterFor loops, that's an excellent choice. But what about early function returns?
Oct 12 2019
On Saturday, 12 October 2019 at 16:48:56 UTC, Jesse Phillips wrote:Sorry forgot the foreach break part. import std.stdio; void main() { import std.algorithm; import std.range; foreach(i; iota(10) .filter!(x => x%2) .until!(x => x>5) .map!"a * a" .filter!(x => x%4)) { i.writeln; break; } } Note this is all done lazily so there is no walking of the iota which happens.This is not exactly what I wanted, but probably the best don't sacrifice as much performance for using functional pipelines. But I still think an `if_not`, `ifnot` or some similar keyword would have it's place. Does anybody agree? Read: might it be worth a DIP?
Oct 14 2019
On Saturday, 12 October 2019 at 16:48:56 UTC, Jesse Phillips wrote:Sorry forgot the foreach break part. import std.stdio; void main() { import std.algorithm; import std.range; foreach(i; iota(10) .filter!(x => x%2) .until!(x => x>5) .map!"a * a" .filter!(x => x%4)) { i.writeln; break; } } Note this is all done lazily so there is no walking of the iota which happens.This is not exactly what I wanted, but probably the best don't sacrifice as much performance for using functional pipelines. But I still think an `if_not`, `ifnot` or some similar keyword would have it's place. Does anybody agree? Read: might it be worth a DIP?
Oct 14 2019
On Monday, 14 October 2019 at 13:00:14 UTC, Dukc wrote:But I still think an `if_not`, `ifnot` or some similar keyword would have it's place. Does anybody agree? Read: might it be worth a DIP?I think a new keyword for this has very little chance. One related idea is making `if !(expression) statement` allowed in the grammar (! outside parentheses) which makes it easier to read fully negated if-statements. It hasn't been that well received though: https://github.com/dlang/dmd/pull/8440 https://forum.dlang.org/post/pgkv3t$15te$1 digitalmars.com
Oct 14 2019
On Monday, 14 October 2019 at 13:26:56 UTC, Dennis wrote:I think a new keyword for this has very little chance. One related idea is making `if !(expression) statement` allowed in the grammar (! outside parentheses) which makes it easier to read fully negated if-statements.Would also do the trick imo, and also avoid a new keyword. Good idea.It hasn't been that well received though: https://github.com/dlang/dmd/pull/8440 https://forum.dlang.org/post/pgkv3t$15te$1 digitalmars.comThanks for those links - I didn't know that `if !(condition)` is
Oct 16 2019
On Monday, 14 October 2019 at 13:00:14 UTC, Dukc wrote:On Saturday, 12 October 2019 at 16:48:56 UTC, Jesse Phillips wrote:Pretty sure you don't need the foreach loop in this example; you can just use `take`: iota(10) .filter!(x => x%2) // ... .take(1) .each!writeln;Sorry forgot the foreach break part. import std.stdio; void main() { import std.algorithm; import std.range; foreach(i; iota(10) .filter!(x => x%2) .until!(x => x>5) .map!"a * a" .filter!(x => x%4)) { i.writeln; break; } } Note this is all done lazily so there is no walking of the iota which happens.This is not exactly what I wanted, but probably the best don't sacrifice as much performance for using functional pipelines.
Oct 14 2019
On Monday, 14 October 2019 at 13:00:14 UTC, Dukc wrote:On Saturday, 12 October 2019 at 16:48:56 UTC, Jesse Phillips wrote:How is not what you want? I realize it doesn't use the same foreach break structure, but then there is no alternative.Sorry forgot the foreach break part. import std.stdio; void main() { import std.algorithm; import std.range; foreach(i; iota(10) .filter!(x => x%2) .until!(x => x>5) .map!"a * a" .filter!(x => x%4)) { i.writeln; break; } } Note this is all done lazily so there is no walking of the iota which happens.This is not exactly what I wanted, but probably the best don't sacrifice as much performance for using functional pipelines.But I still think an `if_not`, `ifnot` or some similar keyword would have it's place. Does anybody agree? Read: might it be worth a DIP?I don't think it is worth it and I do think the range based approach is direction people should head.
Oct 14 2019
On Monday, 14 October 2019 at 15:26:03 UTC, Jesse Phillips wrote:How is not what you want? I realize it doesn't use the same foreach break structure, but then there is no alternative.I think you nailed it - there is apparently no (better) alternative currently. So it's down to discussing DIP or no, and if yes, what should it propose.After reading what Dennis wrote, I think I agree that relaxing parenthesis requirements for `if` statements (and perhaps `while`/`switch`/`with` while on it) is a better way to go. But while I agree that pursuing range pipelines is a good general diretion, sometimes it is just more practical to do stuff in the imperative way - the langauge should not discourage that.But I still think an `if_not`, `ifnot` or some similar keyword would have it's place. Does anybody agree? Read: might it be worth a DIP?I don't think it is worth it and I do think the range based approach is direction people should head.
Oct 16 2019