Array slice assign syntax
Timon Gehr
timon.gehr at gmx.ch
Wed Nov 16 10:47:47 PST 2011
On 11/16/2011 04:24 AM, bearophile wrote:
> The Bugzilla issues that I really care about is a not an useless long list, it's about fifteen items long, and this post is about one of them.
>
> I think current array slice assign syntax is a bit messy, and I think this should be addressed.
>
> Kenji Hara has recently written a very nice program (that here I have modified a bit for clarity) that shows the current array assign situation:
>
>
> import std.stdio, std.typetuple;
>
> void main() {
> writeln("Rhs is an array, is it compilable?");
> writeln("a\t/ b\t\ta=b\ta[]=b\ta=b[]\ta[]=b[]");
>
> foreach (i, Lhs; TypeTuple!(int[3], int[]))
> foreach (j, Rhs; TypeTuple!(int[3], int[])) {
> writef("%s\t/ %s ", Lhs.stringof, Rhs.stringof);
> Lhs a = [0,0,0];
> Rhs b = [1,2,3];
> writef("\t%s", __traits(compiles, { a = b; }));
> writef("\t%s", __traits(compiles, { a[] = b; }));
> writef("\t%s", __traits(compiles, { a = b[]; }));
> writef("\t%s", __traits(compiles, { a[] = b[]; }));
> writeln();
> }
>
>
> writeln("\nRhs is a element, is it compilable?");
> writeln("a\t\t\ta=N\ta[]=N\ta[0..2]=N");
>
> foreach (Lhs; TypeTuple!(int[3], int[])) {
> writef("%s\t\t", Lhs.stringof);
> Lhs a = [0,0,0];
> writef("\t%s", __traits(compiles, { a = 9; }));
> writef("\t%s", __traits(compiles, { a[] = 9; }));
> writef("\t%s", __traits(compiles, { a[0..2] = 9; }));
> writeln();
> }
> }
>
>
>
> Currently (DMD 2.057head, despite I think Walter has not updated DMD version number yet) it prints:
>
>
> Rhs is an array, is it compilable?
> a / b a=b a[]=b a=b[] a[]=b[]
> int[3u] / int[3u] true true true true
> int[3u] / int[] true true true true
> int[] / int[3u] true true true true
> int[] / int[] true true true true
>
> Rhs is a element, is it compilable?
> a a=N a[]=N a[0..2]=N
> int[3u] true true true
> int[] false true true
>
>
>
> This also means this is currently accepted:
>
> void main() {
> int[3] a;
> a = 1;
> assert(a == [1, 1, 1]);
> }
>
>
> While this is not accepted:
>
> void main() {
> int[] b = new int[3];
> b = 1;
> assert(b == [1, 1, 1]); //Error: cannot implicitly convert expression (1) of type int to int[]
> }
>
>
>
> I'd like D to require a[]=1 in that first case too.
>
> I'd like the [] to be required every time an O(n) vector operation is done, for:
> - constancy with all other vector operations among two arrays, that require [];
> - and to avoid unwanted (and not easy to spot in the code) O(n) operations;
> - bugs and confusion in D newbies that don't have memorized all current special cases.
>
> On the other hand Don says that [] is only required for lvalues.
>
> I think this boils to a new table like this:
>
>
> Rhs is an array, is it compilable?
> a / b a=b a[]=b a=b[] a[]=b[]
> int[3u] / int[3u] FALSE true FALSE true
> int[3u] / int[] FALSE true FALSE true
> int[] / int[3u] FALSE true FALSE true
> int[] / int[] true true true true
>
> Rhs is a element, is it compilable?
> a a=N a[]=N a[0..2]=N
> int[3u] FALSE true true
> int[] false true true
>
>
> Now if there's a [] on the left, then it's an O(n) vector operation (like a copy), otherwise it's O(1).
>
> That also means:
>
> void main() {
> int[] a = new int[3];
> int[] b = new int[3];
> a = b; // OK, copies just array fat reference
> }
>
> void main() {
> int[3] a, b;
> a = b; // Not OK, hidden vector op
> }
>
>
> I am not sure this new table is fully correct, but it's a start. Fixes of mistakes are welcomes.
>
> -----------------------
>
> This is an alternative proposal.
>
> On the other hand this vector op syntax doesn't currently compile:
>
> void main() {
> int[3] a, b;
> a[] += b;
> }
>
>
> So if array assign is seen as a normal vector op, then the [] is needed on the right too:
>
>
> Rhs is an array, is it compilable?
> a / b a=b a[]=b a=b[] a[]=b[]
> int[3u] / int[3u] FALSE FALSE FALSE true
> int[3u] / int[] FALSE FALSE FALSE true
> int[] / int[3u] FALSE FALSE FALSE true
> int[] / int[] true FALSE FALSE true
>
> Rhs is a element, is it compilable?
> a a=N a[]=N a[0..2]=N
> int[3u] FALSE true true
> int[] false true true
>
>
> Where the two cases with dynamic arrays are syntax errors to keep more symmetry:
>
> void main() {
> int[] a = new int[3];
> int[] b = new int[3];
> a[] = b; // error
> a = b[]; // error
> }
>
> -----------------------
>
> Lot of time ago I have opened bug issue 3971 on this topic, but that Bugzilla thread contains various mistakes and some parts of it are obsolete.
>
> Bye,
> bearophile
First thing:
int[3] a=3; // kill it!!
Rest:
a[] is _just a shortcut_ for a[0..$]! Are you really suggesting to
disallow slicing?
You are thinking too much in terms of syntax and not enough in terms of
semantics.
They are basically two distinct things involved here:
1. static arrays and lvalue slices
2. dynamic arrays and rvalue slices
1 implies value semantics, 2 implies reference semantics, where value
semantics overrides reference semantics.
Any other distinction is more or less arbitrary. As you pointed out, the
main indicator of distinction is value vs reference semantics of the
performed assignments.
We certainly agree on this:
Rhs is an array, is it compilable?
a / b a=b a[]=b a=b[] a[]=b[]
int[3u] / int[3u] ? ? ? true
int[3u] / int[] ? ? ? true
int[] / int[3u] ? ? ? true
int[] / int[] ? ? ? true
Now, a dynamic array a is equivalent to a[], and a static array b is
equivalent to an lvalue slice b[]=.
This gives the following equivalence classes of operations:
Rhs is an array, is it compilable?
a / b a=b a[]=b a=b[] a[]=b[]
int[3u] / int[3u] 1 1 2 2
int[3u] / int[] 2 2 2 2
int[] / int[3u] 3 1 4 2
int[] / int[] 4 2 4 2
Any of the same class should behave the same.
Now, you suggest in both proposals to allow at least one of class 2 and
at least one of class 4. Filling all those out delivers:
Rhs is an array, is it compilable?
a / b a=b a[]=b a=b[] a[]=b[]
int[3u] / int[3u] (1) (1) true true
int[3u] / int[] true true true true
int[] / int[3u] (3) (1) true true
int[] / int[] true true true true
1 is "assign value to value". 3. is "assign value to reference".
The upper left corner should certainly be true, values of any mutable
type should be able to be assigned to themselves.
This leaves:
Rhs is an array, is it compilable?
a / b a=b a[]=b a=b[] a[]=b[]
int[3u] / int[3u] true true true true
int[3u] / int[] true true true true
int[] / int[3u] (3) true true true
int[] / int[] true true true true
3 is the odd thing out. Now let's think about it, what should:
int[] a;
int[3] b;
a=b;
do?
The answer is, there are two options.
1. implicitly slice b
2. copy b by value into a
One is as arbitrary as the other, so it should be disallowed in a sane
design. Which leaves:
Rhs is an array, is it compilable?
a / b a=b a[]=b a=b[] a[]=b[]
int[3u] / int[3u] true true true true
int[3u] / int[] true true true true
int[] / int[3u] FALSE true true true
int[] / int[] true true true true
Rhs is a element, is it compilable?
a a=N a[]=N a[0..2]=N
int[3u] FALSE true true
int[] false true true
And that is how it should be.
More information about the Digitalmars-d
mailing list