Jump to page: 1 2
Thread overview
[Issue 17771] foreach over const input range fails
Aug 21, 2017
Alex Goltman
Aug 22, 2017
Eyal
Aug 22, 2017
Alex Goltman
Aug 22, 2017
Alex Goltman
Aug 22, 2017
ag0aep6g@gmail.com
Aug 22, 2017
Jonathan M Davis
Feb 23, 2022
Paul Backus
Dec 17, 2022
Iain Buclaw
August 21, 2017
https://issues.dlang.org/show_bug.cgi?id=17771

Steven Schveighoffer <schveiguy@yahoo.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
                 CC|                            |schveiguy@yahoo.com
         Resolution|---                         |INVALID

--- Comment #1 from Steven Schveighoffer <schveiguy@yahoo.com> ---
1. foreach (and D in general) doesn't attempt to change any attributes, it
simply makes a copy via:

auto _r = range;

2. Ranges aren't always copyable to non-const versions implicitly.

So you need to do this yourself or cast:

foreach(x; cast() r)

--
August 21, 2017
https://issues.dlang.org/show_bug.cgi?id=17771

Alex Goltman <alex.goltman@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|INVALID                     |---

--- Comment #2 from Alex Goltman <alex.goltman@gmail.com> ---
If it fails to implicitly copy to a non-const (e.g.  a struct with a pointer inside) then so be it - but why not make it try at least? as in:

Unqual!(typeof(range)) _r = range;

It seems weird that foreach which intentionally copies the range to avoid modifying it will fail because a range is an unmodifiable const.

--
August 21, 2017
https://issues.dlang.org/show_bug.cgi?id=17771

Steven Schveighoffer <schveiguy@yahoo.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|REOPENED                    |RESOLVED
         Resolution|---                         |INVALID

--- Comment #3 from Steven Schveighoffer <schveiguy@yahoo.com> ---
The compiler doesn't attempt to modify attributes in almost any case.

For instance:

auto foo()
{
   const int x = 5;
   return x;
}

auto y = foo(); // y is const(int), even though it could be just int

So this behavior is consistent. You need to tell the compiler to make the copy with the correct attributes.

Note that many many ranges are not implicitly copyable to a mutable version, so this ability would not help in most cases.

One valid enhancement that you could propose is to add a function somewhere in phobos (probably in typecons) that returns a copy of the item with as many qualifiers removed as possible. Then your code could become something like:

foreach(x; unqual(r))

--
August 22, 2017
https://issues.dlang.org/show_bug.cgi?id=17771

Eyal <eyal@weka.io> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |eyal@weka.io

--- Comment #4 from Eyal <eyal@weka.io> ---
Leaving the "const" there (for input ranges) will cause compile-errors
(popFront generally requires mutability).

Removing the attribute will sometimes fail.
Keeping the attribute will always fail.

For the cases where it need not fail - why fail?

It may not be consistent with other behaviors - but there is no case for which it has worse behavior. It is much more useful.

D has many inconsistencies for usefulness:

* Pointers are dereferenced in some contexts but not others
* Integers have valid init values, floats have invalid ones
* Arrays and slices have specific foreach behavior and not the input-range
behavior
* Tuples have special handling in the language

Shouldn't ability to iterate a const/immutable range be important enough to warrant a slight inconsistency?

foreach can simply be defined as calling unqual on its argument.

--
August 22, 2017
https://issues.dlang.org/show_bug.cgi?id=17771

--- Comment #5 from Steven Schveighoffer <schveiguy@yahoo.com> ---
But the problem is that almost all range code is templated, so you are going to test with something that works, and then it will fail with something that doesn't. Subtle inconsistencies like this have a cost.

I'll also note that a const range, in this case, is not actually a range:

const NumRange n;

static assert(!isInputRange!(typeof(n)));

This would result in a stream of bugs like "it works with foreach but not with map, why not?"

--
August 22, 2017
https://issues.dlang.org/show_bug.cgi?id=17771

--- Comment #6 from Steven Schveighoffer <schveiguy@yahoo.com> ---
(In reply to Eyal from comment #4)
> * Pointers are dereferenced in some contexts but not others
Not sure about this one. Perhaps an example can explain better?

> * Integers have valid init values, floats have invalid ones
And it is a cause of problems IMO, especially with generic code.

> * Arrays and slices have specific foreach behavior and not the input-range behavior
Again, causes lots of problems:
string s = "hello";
pragma(msg, typeof(s.front())); // immutable dchar
foreach(x; s)
   pragma(msg, typeof(x)); // immutable char

> * Tuples have special handling in the language
Since obsoleted with a better new feature (static foreach). Again, this is a source of problems, and not meant as a model to follow.

--
August 22, 2017
https://issues.dlang.org/show_bug.cgi?id=17771

Alex Goltman <alex.goltman@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|INVALID                     |---

--- Comment #7 from Alex Goltman <alex.goltman@gmail.com> ---
Actually map does works on const NumRange, since it's declared as:

    auto map(Range)(Range r) if (isInputRange!(Unqual!Range))

Why shouldn't foreach work as well?

It's not a critical bug, but fixing this would remove an annoyance which would make D users much less frustrated. Please don't dismiss this before really evaluating what would it take to fix it, because currently we don't see a reason for this not to just work, as map does already.

--
August 22, 2017
https://issues.dlang.org/show_bug.cgi?id=17771

--- Comment #8 from Steven Schveighoffer <schveiguy@yahoo.com> ---
(In reply to Alex Goltman from comment #7)
> Actually map does works on const NumRange, since it's declared as:
> 
>     auto map(Range)(Range r) if (isInputRange!(Unqual!Range))

Yep, I didn't realize that, ironic that I picked map as the example :)

However, there are other functions that aren't checked this way, and most user code isn't going to do this. It's the wrong way to approach this IMO.

> Why shouldn't foreach work as well?

I'll leave the issue open, as there obviously are some in the core team who find such subtleties worth considering, given the state of std.algorithm. I still don't these kinds of tricks are worth the confusion they can cause, especially when a suitable converter is easily had and used.

--
August 22, 2017
https://issues.dlang.org/show_bug.cgi?id=17771

--- Comment #9 from Alex Goltman <alex.goltman@gmail.com> ---
Asking the user to use a converter makes sense when the user understands
there's a reason to convert.
Here it's counter-intuitive - the error stating foreach doesn't work with a
const actually made me question whether foreach over a range modifies the range
I give it, and I've somehow been unaware of this the whole time.
The current state is more confusing IMHO.

--
August 22, 2017
https://issues.dlang.org/show_bug.cgi?id=17771

--- Comment #10 from Steven Schveighoffer <schveiguy@yahoo.com> ---
It's actually counter-intuitive for the D compiler to strip qualifiers automatically, it almost never happens.

The only place I'm aware of is IFTI for arrays and pointers.

I feel like this is a case of being helpful, but will be confusing because it's not consistent for all ranges. Experience shows that the benefit is not worth the confusion.

But I am not the decider of such things, so I will leave it up to Walter/Andrei to comment.

--
« First   ‹ Prev
1 2