Jump to page: 1 2
Thread overview
Wanted: best way to express default expectations
Oct 10, 2019
Dukc
Oct 10, 2019
Nicholas Wilson
Oct 10, 2019
Dukc
Oct 10, 2019
Dukc
Oct 10, 2019
Dukc
Oct 12, 2019
Jesse Phillips
Oct 12, 2019
Jesse Phillips
Oct 14, 2019
Dukc
Oct 14, 2019
Dukc
Oct 14, 2019
Dennis
Oct 16, 2019
Dukc
Oct 14, 2019
Paul Backus
Oct 14, 2019
Jesse Phillips
Oct 16, 2019
Dukc
October 10, 2019
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?
October 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
October 10, 2019
On Thursday, 10 October 2019 at 11:44:58 UTC, Nicholas Wilson wrote:
>
> https://dlang.org/phobos/std_algorithm_iteration.html#.filter

For loops, that's an excellent choice. But what about early function returns?
October 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:
>>
>> https://dlang.org/phobos/std_algorithm_iteration.html#.filter
>
> For loops, that's an excellent choice. But what about early function returns?

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.
October 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:
>> On Thursday, 10 October 2019 at 11:44:58 UTC, Nicholas Wilson wrote:
>>>
>>> https://dlang.org/phobos/std_algorithm_iteration.html#.filter
>>
>> For loops, that's an excellent choice. But what about early function returns?
>
> 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#.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.
October 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.
October 12, 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:
>> On Thursday, 10 October 2019 at 11:44:58 UTC, Nicholas Wilson wrote:
>>>
>>> https://dlang.org/phobos/std_algorithm_iteration.html#.filter
>>
>> For loops, that's an excellent choice. But what about early function returns?
>
> 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.

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;
}

October 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:
>> 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:
>>>>
>>>> https://dlang.org/phobos/std_algorithm_iteration.html#.filter
>>>
>>> For loops, that's an excellent choice. But what about early function returns?
>>
>> 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.

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.
October 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 alternative overall. At least it's better than in C#, as you 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?
October 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 alternative overall. At least it's better than in C#, as you 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?
« First   ‹ Prev
1 2