Why D const is annoying
Jonathan M Davis
jmdavisProg at gmx.com
Sat Dec 10 12:25:25 PST 2011
On Saturday, December 10, 2011 05:45:11 bearophile wrote:
> Timon Gehr:
> > Just slice the const array to get a range. The specialization for ranges
> > does not have the bug.
> >
> > import std.algorithm;
> > void main() {
> >
> > const arr = [1, 2, 3];
> > reduce!"a*b"(arr[]); // It works.
> >
> > }
>
> Wasn't arr a range already?
Per isInputRange!(typeof(arr)), no. It has the right functions, but it can't
use them, because it's const. A const range is essentially useless, because
you can't iterate over it.
When a template is instantiated, it's instantiated on the exact types that
it's given. So, if you given a const or immutable array, it's going to
instantiate on that type, even though it _could_ theoretically instantiate
itself with a mutable array with const or immutable elements. And since, a
const or immutable array won't work as a range, the template instantiation
fails.
The range-based functions in std.array and std.string work with const and
immutable arrays simply because they are either coded specifically for arrays
or have specializations for them. You need a function which takes const(T)[]
rather than one which takes const T[]. std.array and std.string typically take
const(T)[] rather than const T[], whereas a more general range function is
taking R, which in the case above is determined to be const int[], which won't
work as a range.
It used to be that the slice of an array was the exact type of that array,
meaning Timon's solution wouldn't work without a cast, because the slice would
be just as const as the original. Fortunately, that has been changed, so now a
slice of a const or immutable array will have its elements be const or
immutable but not the slice itself. So, now const and immutable arrays are
like static arrays in that you need to slice them to use them with range-based
functions, but you can't use them directly with range-based functions.
A bigger problem is const or immutable ranges which are structs. Unless the
programmer who defined the range type managed to have an opSlice which returned
a version with const or immutable elements but where the range itself wasn't
const or immutable (i.e. a tail-const slice), then you can't even get slicing
to work. And even if templates were improved to the point that they would
instantiate const int[] as const(int)[] (which I expect is a change which will
never happen due to the difficulties in doing so), that wouldn't solve the
problem for ranges which aren't arrays.
Also, const and immutable ranges which aren't arrays which have no opSlice
will _never_ work, because there's no way to get a mutable slice of them to
operate on, so you're stuck with a const or immutable range.
Really what this means is that you need to slice const and immutable ranges
when you pass them to range-based functions, and then as long as they're
arrays or their opSlices have been defined properly, it'll work just fine.
If ranges had been designed more like slists (i.e. they have head and tail
rather than front and popFront), then they would have been inherently tail-
const and we wouldn't be having these problems. But that's less efficent,
because you have to copy the range every time that you want to remove an
element from it. Regardless, it's too late now. Ranges are too heavily used
with their current API for us to change it that drastically now.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list