November 25, 2016
On Friday, 25 November 2016 at 11:10:44 UTC, Timon Gehr wrote:
> You can just as easily edit the while condition. I use it because "unconditional loop" is less silly than "loop until true is false".

Unconditional loop can be implemented in 3 possible ways :)
1. skip it entirely: since there's no condition to check, can't decide if should enter at all
2. run once: since there's no condition to check, can't decide if should repeat - becomes just an unconditional block statement
3. infinite loop as if the condition is always true - until it's false
November 25, 2016
On Friday, November 25, 2016 10:46:15 Steven Schveighoffer via Digitalmars-d wrote:
> On 11/25/16 8:24 AM, Jonathan M Davis via Digitalmars-d wrote:
> > On Friday, November 25, 2016 07:59:07 Andrei Alexandrescu via
> > Digitalmars-d>
> > wrote:
> >> On 11/25/2016 07:53 AM, Dennis Ritchie wrote:
> >>> https://github.com/dlang/phobos/blob/master/std/algorithm/comparison.d
> >>> #L
> >>> 591
> >>
> >> I like that function. If I were to review it now, I'd approve with
> >> these
> >> nits:
> >>
> >> * drop the parens for popFront
> >
> > I would point out that technically, that breaks the range API. isInputRange requires that popFront be callable with parens, but it does not require that it be callable without parens. So, someone could define popFront as a public member variable with an overloaded opCall. That would not compile if it were used with code that called popFront without parens even though it would compile with isInputRange.
>
> This is a misunderstanding. The missing parens is for *usage* of the range, not *definition* of the range. It won't affect isInputRange at all.

It's not a misunderstanding.

template isInputRange(R)
{
    enum bool isInputRange = is(typeof(
    (inout int = 0)
    {
        R r = R.init;     // can define a range object
        if (r.empty) {}   // can test for empty
        r.popFront();     // can invoke popFront()
        auto h = r.front; // can get the front of the range
    }));
}

calls popFront with parens. That means that it's perfectly legal per isInputRange to define popFront such that it's a callable that does _not_ work with optional parens. So, if it were a member variable that defined opCall or was a delegate, then to use it, the parens are required. That means that I could define a range that passes isInputRange but does not work with code that called popFront without parens. As isInputRange is currently defined, it's perfectly legal.

> This case you have of defining a popFront member variable with opCall -- don't do that, it will break things (I'm sure there are already many places where popFront is called without parens). I don't think that's a case that we need worry about.

It's a case that's currently legal. It's just not one that's particularly likely.  We can certainly change isInputRange to make such a case illegal. But as it stands, someone could have defined such a range, and it would work with a _lot_ range-based code, because it's very common to call popFront with parens, and if such a range were being used with a function in Phobos, and that function were changed to call popFront without parens, it would break code.

So, if we want to change the definition of isInputRange to get rid of this problem, fine. I'm just pointing out that as isInputRange is currently defined, calling popFront without parens would result in that code not working with ranges that are perfectly legal per isInputRange and could be perfectly legitimate ranges in all aspects of how they function.

And calling empty _with_ parens even though it would be legal in most cases right now would have the exact same problem except in reverse. Everything wasn't a callable wouldn't work - including stuff like empty on infinite ranges.

D allows us to be lax with syntax to some extent, but we need to be careful with how we deal with that in generic code, or we're going to end up with stuff that should work but doesn't just because parens happened to have been used or not in a particular piece of code.

- Jonathan M Davis

November 25, 2016
On 11/25/2016 11:47 AM, Jonathan M Davis via Digitalmars-d wrote:
> template isInputRange(R)
> {
>     enum bool isInputRange = is(typeof(
>     (inout int = 0)
>     {
>         R r = R.init;     // can define a range object
>         if (r.empty) {}   // can test for empty
>         r.popFront();     // can invoke popFront()
>         auto h = r.front; // can get the front of the range
>     }));
> }
>
> calls popFront with parens.

Jonathan, could you please make a PR to remove the parens. Thanks. -- Andrei
November 25, 2016
On Friday, November 25, 2016 10:03:26 Andrei Alexandrescu via Digitalmars-d wrote:
> On 11/25/16 8:24 AM, Jonathan M Davis via Digitalmars-d wrote:
> > I would point out that technically, that breaks the range API.
>
> We need to change the range API then. -- Andrei

We can certainly do that. I'm just pointing out that as it stands, it's legal to declare popFront to be a callable that doesn't work with optional parens, and as long as that's true, calling popFront without parens means that the code won't work with perfectly legitimate ranges. So, unless/until isInputRange is changed, I'd say that calling popFront without parens is a bad idea.

- Jonathan M Davis

November 25, 2016
On Friday, November 25, 2016 11:01:56 Andrei Alexandrescu via Digitalmars-d wrote:
> On 11/25/16 10:29 AM, Adam D. Ruppe wrote:
> > Let's just close the book and officially put the status quo on optional parenthesis in stone: they are optional on zero-argument calls, regardless of @property, and that isn't going to change.
>
> Agreed. -- Andrei

That's fine, but it doesn't help any when the callable can't be used with optional parens - which is the case with a type overloading opCall as well as function pointers and delegates. And the real problem here isn't whether someone chooses to call a function with parens or not in some chunk of their code. The problem is whether _generic_ code does it. If you're calling something with parens in generic code, then you need to be able to rely on it working with parens regardless of the types of the function arguments. And if you're calling it without parens, then you need to be able to rely on it working _without_ parens regardless of the types of the function arguments.

Stuff like empty, save, and length in the range API all of the same problem as popFront and popBack except in reverse. In their case, they need to be called without parens, or the code won't work with some ranges (the most obvious case being for empty being infinite ranges, and the most obvious case for length being dynamic arrays).

So, restricting popFront and popBack to being functions so that they can always be called without parens is fine, but it's still the case that you can't be completely lax with where you do or don't use parens in generic code.

- Jonathan M Davis

November 25, 2016
On Friday, 25 November 2016 at 15:46:15 UTC, Steven Schveighoffer wrote:
> This case you have of defining a popFront member variable with opCall -- don't do that, it will break things (I'm sure there are already many places where popFront is called without parens). I don't think that's a case that we need worry about.

Seems like not that many.
grep -r "popFront;" *
algorithm/comparison.d:            r2.popFront;
algorithm/comparison.d:            r1.popFront;
algorithm/comparison.d:           r1.popFront;
algorithm/comparison.d:           r2.popFront;
algorithm/mutation.d:        void popFront() { data.popFront; }
experimental/allocator/typed.d:    front, popFront;
experimental/ndslice/selection.d:                        val.popFront;
experimental/ndslice/selection.d:        elems2.popFront;
experimental/ndslice/selection.d:                elems2.popFront;
experimental/ndslice/selection.d:    elems.popFront;
experimental/ndslice/slice.d:        slice.popFront;
experimental/ndslice/slice.d:                slice.popFront;
experimental/ndslice/slice.d:                value.popFront;
experimental/ndslice/slice.d:            slice.popFront;
experimental/ndslice/slice.d:            value.popFront;
experimental/ndslice/slice.d:            slice.popFront;
experimental/ndslice/slice.d:                slice.popFront;
experimental/ndslice/slice.d:            slice.popFront;
experimental/ndslice/slice.d:            slice.popFront;
experimental/ndslice/slice.d:                slice.popFront;
experimental/ndslice/slice.d:            slice.popFront;
range/package.d:            return condition ? r1.popFront : r2.popFront;

The last one is strange.
November 25, 2016
On 11/25/16 11:47 AM, Jonathan M Davis via Digitalmars-d wrote:
> On Friday, November 25, 2016 10:46:15 Steven Schveighoffer via Digitalmars-d
> wrote:
>> On 11/25/16 8:24 AM, Jonathan M Davis via Digitalmars-d wrote:

>>> I would point out that technically, that breaks the range API.
>>> isInputRange requires that popFront be callable with parens, but it
>>> does not require that it be callable without parens. So, someone could
>>> define popFront as a public member variable with an overloaded opCall.
>>> That would not compile if it were used with code that called popFront
>>> without parens even though it would compile with isInputRange.
>>
>> This is a misunderstanding. The missing parens is for *usage* of the
>> range, not *definition* of the range. It won't affect isInputRange at all.
>
> It's not a misunderstanding.
>
> template isInputRange(R)
> {
>     enum bool isInputRange = is(typeof(
>     (inout int = 0)
>     {
>         R r = R.init;     // can define a range object
>         if (r.empty) {}   // can test for empty
>         r.popFront();     // can invoke popFront()
>         auto h = r.front; // can get the front of the range
>     }));
> }
>
> calls popFront with parens. That means that it's perfectly legal per
> isInputRange to define popFront such that it's a callable that does _not_
> work with optional parens. So, if it were a member variable that defined
> opCall or was a delegate, then to use it, the parens are required. That
> means that I could define a range that passes isInputRange but does not work
> with code that called popFront without parens. As isInputRange is currently
> defined, it's perfectly legal.

It's perfectly legal to make a popFront that destroys your entire program. Or that doesn't actually pop the front (see old generate() function). So what? I can't see the point of going out of your way to make a popFront member that supports opCall, when you can use a function.

Note: there are cases out there where popFront is called without parentheses. It has never broken any code. This should be a hint that what you are concerned about doesn't happen in practice.

-Steve
November 25, 2016
On Fri, Nov 25, 2016 at 03:20:24AM -0800, Jonathan M Davis via Digitalmars-d wrote:
> On Friday, November 25, 2016 12:10:44 Timon Gehr via Digitalmars-d wrote:
> > On 25.11.2016 11:33, Claude wrote:
> > > ...
> > >
> > > Between "for(;;)", "while(true)" and "do while(true)", I would use the
> > > "while (true) { }" for pure readability and semantic reasons.
> > > ...
> >
> > What semantic reasons?
> 
> Probably the complete lack of a condition to test in for(;;). I
> confess that I was shocked when I found out that it was legal to have
> a for loop without a condition. That seems like doing while() or if(),
> which makes no sense.

Sure it does. A conditionless loop is exactly what an infinite loop is. A while-loop, by definition, has a condition (loop *while* something is true, stop looping when it's no longer true), and an if-statement by definition is conditional. Neither match the nature of an infinite loop, which is infinite because it has no exit condition.

Well, OK, a loop can also be infinite if the exit condition just happens to be always true, but I feel that is more deceptive than a conditionless loop when it's deliberately written, since a conditional loop whose condition happens to be always true sounds to me like a bug or a subversion (we intend this loop to stop when condition X becomes false, but bwahaha I arranged for X to never become false!). When a loop is *deliberately* meant to be infinite, I see a conditionless loop as a more faithful representation of that intent.

Therefore I prefer `for(;;)` over any of the `while(true)`, `while(1)`,
etc., variations. It conveys intent in a more straightforward, up-front
way.

If it helps you stomach it, you could interpret the `(;;)` as a funny way of saying "ever", so in essence you're saying `for-ever { ... }`.


T

-- 
Computers shouldn't beep through the keyhole.
November 25, 2016
On Friday, November 25, 2016 11:51:24 Andrei Alexandrescu via Digitalmars-d wrote:
> Jonathan, could you please make a PR to remove the parens. Thanks.

Done.

https://github.com/dlang/phobos/pull/4925

- Jonathan M Davis

November 25, 2016
I don't mean an infinite loop for (;;), and violation of the concepts of structured programming, which put forward Dijkstra.

for (;;) {
    ...
    if (condition) {
        break;
    }
    ...
}

outer: for (;;) {
    ...
    if (condition) {
        goto outer;
    }
    ...
}

I.e. in system programming and I want to use `break` or `goto`, because here we use imperative rather than declarative approach.
The following question arises. Are structured programming concept of Dijkstra's right for writing system software?