www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - T[new] misgivings

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
I talked to Walter about T[new] today and it seems we are having a 
disagreement.

The problem is that I believe T[new] is a container, whereas Walter 
believes T[new] is nothing but a slice with a couple of extra operations.

Paradoxically this seems to be conducive to subtle efficiency issues. 
For example, consider:

int[new] a;
...
a = [1, 2, 3];

What should that do?

Walter: T[new] is a slice with benefits, assignment for slices rebinds 
the slice, therefore the assignment must do the same. In this case, the 
assignments allocate a new array and make a refer to that array. 
Whatever old array a referred to will continue to live wherever it was.

Me: T[new] is a container, therefore the assignment must resize the 
container from whatever size it had to 3 and then write 1, 2, 3 to its 
three slots.

I guess each of us has a point, but this is a setup for an increasingly 
unpleasant situation. Here's the dialog as it happened.

A: Ok, then how do I say the common operation "I want to overwrite 
whatever the array had with 1, 2, 3"? I can only presume there must be 
an obvious and simple way to do so, and I thought a = [1, 2, 3] was the 
obvious syntax to achieve that.

W: No, you must write

a[] = [1, 2, 3];

A: But that only works if the container already had length 3. So what I 
need to do is this:

a.length = 3;
a[] = [1, 2, 3];

A: But that is inefficient if the array had length less than 3 because 
it means double assignment

W: Nobody complained about it with slices.

A: So if I do want something that does the obvious operation "Whatever 
that array had, make it now have 1, 2, 3 as it contents" at a reasonable 
cost I need to call an elaborate function that is highly nontrivial to 
write?

W: Looks like so.


assignment to arrays. Call the assign() function"?

W: Nobody complained about it with slices.

===============

This goes into something more interesting that I thought of after the 
conversation. Consider:

T[new] a;
T[] b;
...
a = b;

What should that do?


Andrei
Oct 15 2009
next sibling parent reply Rainer Deyke <rainerd eldwood.com> writes:
Andrei Alexandrescu wrote:
 int[new] a;
 ....
 a = [1, 2, 3];
 
 What should that do?
This question can be rephrased as, "should 'int[new]' be a reference type or a value type (or something else)?" If 'int[new]' is a reference type, then it must rebind, because that's what assignment does for reference types. If 'int[new]' is a value type, then it must modify the array in place, because that's all it can do. If 'int[new]' is neither a reference type nor a value type, then we're back to (some of) the problems with slices. To answer the rephrased question: 'int[new]' should be a value type.
 W: Nobody complained about it with slices.
FWIW, I found arrays in D1 so completely broken that I didn't it worth the effort to complain about every little detail. Everything about them was wrong. I consider them a textbook example of what not to do. -- Rainer Deyke - rainerd eldwood.com
Oct 15 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Rainer Deyke wrote:
 Andrei Alexandrescu wrote:
 int[new] a;
 ....
 a = [1, 2, 3];

 What should that do?
This question can be rephrased as, "should 'int[new]' be a reference type or a value type (or something else)?" If 'int[new]' is a reference type, then it must rebind, because that's what assignment does for reference types. If 'int[new]' is a value type, then it must modify the array in place, because that's all it can do. If 'int[new]' is neither a reference type nor a value type, then we're back to (some of) the problems with slices. To answer the rephrased question: 'int[new]' should be a value type.
Well Walter and I agreed they should be pass-by-reference. That doesn't mean they must be references, and the fact that the simplest syntax has the worst efficiency reminds me of iostreams.
 W: Nobody complained about it with slices.
FWIW, I found arrays in D1 so completely broken that I didn't it worth the effort to complain about every little detail. Everything about them was wrong. I consider them a textbook example of what not to do.
My perception is that you're in a minority. Anyway, if there's something that T[new] can help with, let us know. Andrei
Oct 15 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 To answer the rephrased question: 'int[new]' should be a value type.
Well Walter and I agreed they should be pass-by-reference. That doesn't mean they must be references, and the fact that the simplest syntax has the worst efficiency reminds me of iostreams.
Given that D already has both value types and reference types, the addition of types that are passed by reference but otherwise act as value types actually seems reasonable. It make the language more orthogonal. Classes have one set of attributes. Structs have another. If the language absolutely needs to support both sets of attributes, I should at least be able to mix and match between them. So, what's the syntax for user-defined value types that are passed by reference going to be? ref struct? opPass?
 FWIW, I found arrays in D1 so completely broken that I didn't it worth
 the effort to complain about every little detail.  Everything about them
 was wrong.  I consider them a textbook example of what not to do.
My perception is that you're in a minority. Anyway, if there's something that T[new] can help with, let us know.
I was under the impression that arrays were generally considered broken, which is why 'T[new]' is now being introduced. -- Rainer Deyke - rainerd eldwood.com
Oct 15 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Rainer Deyke wrote:
 Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 To answer the rephrased question: 'int[new]' should be a value type.
Well Walter and I agreed they should be pass-by-reference. That doesn't mean they must be references, and the fact that the simplest syntax has the worst efficiency reminds me of iostreams.
Given that D already has both value types and reference types, the addition of types that are passed by reference but otherwise act as value types actually seems reasonable. It make the language more orthogonal. Classes have one set of attributes. Structs have another. If the language absolutely needs to support both sets of attributes, I should at least be able to mix and match between them. So, what's the syntax for user-defined value types that are passed by reference going to be? ref struct? opPass?
No need for new syntax. T[new] is a struct that has a pointer inside.
 FWIW, I found arrays in D1 so completely broken that I didn't it worth
 the effort to complain about every little detail.  Everything about them
 was wrong.  I consider them a textbook example of what not to do.
My perception is that you're in a minority. Anyway, if there's something that T[new] can help with, let us know.
I was under the impression that arrays were generally considered broken, which is why 'T[new]' is now being introduced.
There is a pernicious issue with ~= and slices. T[new] aims at fixing that. Andrei
Oct 15 2009
parent Rainer Deyke <rainerd eldwood.com> writes:
Andrei Alexandrescu wrote:
 Rainer Deyke wrote:
 So, what's the syntax for user-defined value types that are passed by
 reference going to be?  ref struct?  opPass?
No need for new syntax. T[new] is a struct that has a pointer inside.
Without new syntax, there's no way to distinguish between assignment and argument passing. Either 'T[new]' is a value type or 'T[new]' is a reference type. Or 'T[new]' is a messy hybrid, like arrays in D1. There is no way to create a *clean* hybrid with the language facilities in D2. If 'T[new]' has reference semantics, then assignment rebinds the reference. This actually works out fairly well, except for the problem that 'T[new]' has reference semantics. Preferred syntax: assert(is(typeof([1, 2, 3]) == int[new])); int[new] a = [1, 2, 3]; // Rebinds. Syntax for people who insist that array literals should be constant: assert(is(typeof(create_array(1, 2, 3)) == int[new])); int[new] a = create_array(1, 2, 3); // Rebinds. -- Rainer Deyke - rainerd eldwood.com
Oct 15 2009
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Andrei Alexandrescu wrote:
 This goes into something more interesting that I thought of after the 
 conversation. Consider:
 
 T[new] a;
 T[] b;
 ...
 a = b;
 
 What should that do?
Error. T[] cannot be implicitly converted to T[new]
Oct 15 2009
next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Thu, 15 Oct 2009 23:16:56 -0400, Walter Bright  
<newshound1 digitalmars.com> wrote:

 Andrei Alexandrescu wrote:
 This goes into something more interesting that I thought of after the  
 conversation. Consider:
  T[new] a;
 T[] b;
 ...
 a = b;
  What should that do?
Error. T[] cannot be implicitly converted to T[new]
I agree.
Oct 15 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 Andrei Alexandrescu wrote:
 This goes into something more interesting that I thought of after the 
 conversation. Consider:

 T[new] a;
 T[] b;
 ...
 a = b;

 What should that do?
Error. T[] cannot be implicitly converted to T[new]
Then your argument building on similarity between the two is weakened. T[new] a; T[] b; ... a = [1, 2, 3]; b = [1, 2, 3]; Central to your argument was that the two must do the same thing. Since now literals are in a whole new league (they aren't slices because slices can't be assigned to arrays), the cornerstone of your argument goes away. Andrei
Oct 15 2009
next sibling parent reply Rainer Deyke <rainerd eldwood.com> writes:
Andrei Alexandrescu wrote:
 Then your argument building on similarity between the two is weakened.
 
 T[new] a;
 T[] b;
 ....
 a = [1, 2, 3];
 b = [1, 2, 3];
 
 Central to your argument was that the two must do the same thing. Since
 now literals are in a whole new league (they aren't slices because
 slices can't be assigned to arrays), the cornerstone of your argument
 goes away.
Actually [1, 2, 3] looks more like an array than a slice to me. Arrays can be assigned to slices, no? -- Rainer Deyke - rainerd eldwood.com
Oct 15 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Rainer Deyke wrote:
 Andrei Alexandrescu wrote:
 Then your argument building on similarity between the two is weakened.

 T[new] a;
 T[] b;
 ....
 a = [1, 2, 3];
 b = [1, 2, 3];

 Central to your argument was that the two must do the same thing. Since
 now literals are in a whole new league (they aren't slices because
 slices can't be assigned to arrays), the cornerstone of your argument
 goes away.
Actually [1, 2, 3] looks more like an array than a slice to me. Arrays can be assigned to slices, no?
My point exactly. Andrei
Oct 15 2009
prev sibling parent reply Jeremie Pelletier <jeremiep gmail.com> writes:
Andrei Alexandrescu wrote:
 Walter Bright wrote:
 Andrei Alexandrescu wrote:
 This goes into something more interesting that I thought of after the 
 conversation. Consider:

 T[new] a;
 T[] b;
 ...
 a = b;

 What should that do?
Error. T[] cannot be implicitly converted to T[new]
Then your argument building on similarity between the two is weakened. T[new] a; T[] b; ... a = [1, 2, 3]; b = [1, 2, 3]; Central to your argument was that the two must do the same thing. Since now literals are in a whole new league (they aren't slices because slices can't be assigned to arrays), the cornerstone of your argument goes away. Andrei
Simple, assignment to a fails 'cannot cast T[3] to T[new]'. It's already consistent with slices of different types: char[] a = "foo"; // error, cannot cast immutable(char)[] to char[] int[new] b = [1, 2, 3]; // error, cannot cast int[3] to int[new] you have to do: char[] a = "foo".dup; int[new] b = [1, 2, 3].dup; Jeremie
Oct 15 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Jeremie Pelletier wrote:
 Andrei Alexandrescu wrote:
 Walter Bright wrote:
 Andrei Alexandrescu wrote:
 This goes into something more interesting that I thought of after 
 the conversation. Consider:

 T[new] a;
 T[] b;
 ...
 a = b;

 What should that do?
Error. T[] cannot be implicitly converted to T[new]
Then your argument building on similarity between the two is weakened. T[new] a; T[] b; ... a = [1, 2, 3]; b = [1, 2, 3]; Central to your argument was that the two must do the same thing. Since now literals are in a whole new league (they aren't slices because slices can't be assigned to arrays), the cornerstone of your argument goes away. Andrei
Simple, assignment to a fails 'cannot cast T[3] to T[new]'. It's already consistent with slices of different types: char[] a = "foo"; // error, cannot cast immutable(char)[] to char[] int[new] b = [1, 2, 3]; // error, cannot cast int[3] to int[new] you have to do: char[] a = "foo".dup; int[new] b = [1, 2, 3].dup; Jeremie
I'd be _very_ unhappy to have to explain to people how in the world we managed to make the most intuitive syntax not work at all. Andrei
Oct 15 2009
parent reply Jeremie Pelletier <jeremiep gmail.com> writes:
Andrei Alexandrescu wrote:
 Jeremie Pelletier wrote:
 Andrei Alexandrescu wrote:
 Walter Bright wrote:
 Andrei Alexandrescu wrote:
 This goes into something more interesting that I thought of after 
 the conversation. Consider:

 T[new] a;
 T[] b;
 ...
 a = b;

 What should that do?
Error. T[] cannot be implicitly converted to T[new]
Then your argument building on similarity between the two is weakened. T[new] a; T[] b; ... a = [1, 2, 3]; b = [1, 2, 3]; Central to your argument was that the two must do the same thing. Since now literals are in a whole new league (they aren't slices because slices can't be assigned to arrays), the cornerstone of your argument goes away. Andrei
Simple, assignment to a fails 'cannot cast T[3] to T[new]'. It's already consistent with slices of different types: char[] a = "foo"; // error, cannot cast immutable(char)[] to char[] int[new] b = [1, 2, 3]; // error, cannot cast int[3] to int[new] you have to do: char[] a = "foo".dup; int[new] b = [1, 2, 3].dup; Jeremie
I'd be _very_ unhappy to have to explain to people how in the world we managed to make the most intuitive syntax not work at all. Andrei
I agree it can be confusing, the first time i tried to assign a string literal to a char[] in D2 I had to pause for a second to understand what was happening :) But what I don't like is that assigning memory from the static data segment to a resizable array isn't safe. Unless the GC can detect that the memory it is trying to resize isn't part of the heap and automatically create a new allocation for it, you're gonna have nasty side effects. The compiler could also implicitly copy the slice, but then it should also automatically copy a "foo" literal when assigned to char[] to keep consistent. Jeremie
Oct 15 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Jeremie Pelletier wrote:
 Andrei Alexandrescu wrote:
 Jeremie Pelletier wrote:
 Andrei Alexandrescu wrote:
 Walter Bright wrote:
 Andrei Alexandrescu wrote:
 This goes into something more interesting that I thought of after 
 the conversation. Consider:

 T[new] a;
 T[] b;
 ...
 a = b;

 What should that do?
Error. T[] cannot be implicitly converted to T[new]
Then your argument building on similarity between the two is weakened. T[new] a; T[] b; ... a = [1, 2, 3]; b = [1, 2, 3]; Central to your argument was that the two must do the same thing. Since now literals are in a whole new league (they aren't slices because slices can't be assigned to arrays), the cornerstone of your argument goes away. Andrei
Simple, assignment to a fails 'cannot cast T[3] to T[new]'. It's already consistent with slices of different types: char[] a = "foo"; // error, cannot cast immutable(char)[] to char[] int[new] b = [1, 2, 3]; // error, cannot cast int[3] to int[new] you have to do: char[] a = "foo".dup; int[new] b = [1, 2, 3].dup; Jeremie
I'd be _very_ unhappy to have to explain to people how in the world we managed to make the most intuitive syntax not work at all. Andrei
I agree it can be confusing, the first time i tried to assign a string literal to a char[] in D2 I had to pause for a second to understand what was happening :) But what I don't like is that assigning memory from the static data segment to a resizable array isn't safe. Unless the GC can detect that the memory it is trying to resize isn't part of the heap and automatically create a new allocation for it, you're gonna have nasty side effects. The compiler could also implicitly copy the slice, but then it should also automatically copy a "foo" literal when assigned to char[] to keep consistent. Jeremie
Speaking of which, a funny fallout of this a = [1, 2, 3] thing is that we, while striving to avoid all hidden allocations, ended up doing the worst hidden allocation with the simplest and most intuitive syntax. Andrei
Oct 15 2009
parent Sergey Gromov <snake.scaly gmail.com> writes:
Thu, 15 Oct 2009 23:18:22 -0500, Andrei Alexandrescu wrote:

 Jeremie Pelletier wrote:
 Andrei Alexandrescu wrote:
 Jeremie Pelletier wrote:
 Andrei Alexandrescu wrote:
 Walter Bright wrote:
 Andrei Alexandrescu wrote:
 This goes into something more interesting that I thought of after 
 the conversation. Consider:

 T[new] a;
 T[] b;
 ...
 a = b;

 What should that do?
Error. T[] cannot be implicitly converted to T[new]
Then your argument building on similarity between the two is weakened. T[new] a; T[] b; ... a = [1, 2, 3]; b = [1, 2, 3]; Central to your argument was that the two must do the same thing. Since now literals are in a whole new league (they aren't slices because slices can't be assigned to arrays), the cornerstone of your argument goes away. Andrei
Simple, assignment to a fails 'cannot cast T[3] to T[new]'. It's already consistent with slices of different types: char[] a = "foo"; // error, cannot cast immutable(char)[] to char[] int[new] b = [1, 2, 3]; // error, cannot cast int[3] to int[new] you have to do: char[] a = "foo".dup; int[new] b = [1, 2, 3].dup; Jeremie
I'd be _very_ unhappy to have to explain to people how in the world we managed to make the most intuitive syntax not work at all. Andrei
I agree it can be confusing, the first time i tried to assign a string literal to a char[] in D2 I had to pause for a second to understand what was happening :) But what I don't like is that assigning memory from the static data segment to a resizable array isn't safe. Unless the GC can detect that the memory it is trying to resize isn't part of the heap and automatically create a new allocation for it, you're gonna have nasty side effects. The compiler could also implicitly copy the slice, but then it should also automatically copy a "foo" literal when assigned to char[] to keep consistent. Jeremie
Speaking of which, a funny fallout of this a = [1, 2, 3] thing is that we, while striving to avoid all hidden allocations, ended up doing the worst hidden allocation with the simplest and most intuitive syntax.
How about this specification: [1, 2, 3] is an array literal which *allocates* an array but the allocation can be optimized away if assigned to a slice.
Oct 17 2009
prev sibling next sibling parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Thu, 15 Oct 2009 22:55:07 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 I talked to Walter about T[new] today and it seems we are having a  
 disagreement.

 The problem is that I believe T[new] is a container, whereas Walter  
 believes T[new] is nothing but a slice with a couple of extra operations.

 Paradoxically this seems to be conducive to subtle efficiency issues.  
 For example, consider:

 int[new] a;
 ...
 a = [1, 2, 3];

 What should that do?

 Walter: T[new] is a slice with benefits, assignment for slices rebinds  
 the slice, therefore the assignment must do the same. In this case, the  
 assignments allocate a new array and make a refer to that array.  
 Whatever old array a referred to will continue to live wherever it was.

 Me: T[new] is a container, therefore the assignment must resize the  
 container from whatever size it had to 3 and then write 1, 2, 3 to its  
 three slots.

 I guess each of us has a point, but this is a setup for an increasingly  
 unpleasant situation. Here's the dialog as it happened.

 A: Ok, then how do I say the common operation "I want to overwrite  
 whatever the array had with 1, 2, 3"? I can only presume there must be  
 an obvious and simple way to do so, and I thought a = [1, 2, 3] was the  
 obvious syntax to achieve that.

 W: No, you must write

 a[] = [1, 2, 3];

 A: But that only works if the container already had length 3. So what I  
 need to do is this:

 a.length = 3;
 a[] = [1, 2, 3];

 A: But that is inefficient if the array had length less than 3 because  
 it means double assignment

 W: Nobody complained about it with slices.

 A: So if I do want something that does the obvious operation "Whatever  
 that array had, make it now have 1, 2, 3 as it contents" at a reasonable  
 cost I need to call an elaborate function that is highly nontrivial to  
 write?

 W: Looks like so.


 assignment to arrays. Call the assign() function"?

 W: Nobody complained about it with slices.

 ===============

 This goes into something more interesting that I thought of after the  
 conversation. Consider:

 T[new] a;
 T[] b;
 ...
 a = b;

 What should that do?


 Andrei
I like (and have used) the opSliceAssign syntax to represent by value/copy assignment as opposed to opAssign's by reference syntax. You could always define T[new] auto-resize in the case of a[] = b, but then you'd have to decide if that behavior should be extended to slices.
Oct 15 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Robert Jacques wrote:
 I like (and have used) the opSliceAssign syntax to represent by 
 value/copy assignment as opposed to opAssign's by reference syntax. You 
 could always define T[new] auto-resize in the case of a[] = b, but then 
 you'd have to decide if that behavior should be extended to slices.
I could. Walter doesn't wanna. He says he wants a[] = b[] to generate an unchecked memcpy in release mode. Andrei
Oct 15 2009
prev sibling next sibling parent Jason House <jason.james.house gmail.com> writes:
Andrei Alexandrescu Wrote:

 I talked to Walter about T[new] today and it seems we are having a 
 disagreement.
 
 The problem is that I believe T[new] is a container, whereas Walter 
 believes T[new] is nothing but a slice with a couple of extra operations.
 
 Paradoxically this seems to be conducive to subtle efficiency issues. 
 For example, consider:
 
 int[new] a;
 ...
 a = [1, 2, 3];
 
 What should that do?
Allocate an array
 Walter: T[new] is a slice with benefits, assignment for slices rebinds 
 the slice, therefore the assignment must do the same. In this case, the 
 assignments allocate a new array and make a refer to that array. 
 Whatever old array a referred to will continue to live wherever it was.
 
 Me: T[new] is a container, therefore the assignment must resize the 
 container from whatever size it had to 3 and then write 1, 2, 3 to its 
 three slots.
 
 I guess each of us has a point, but this is a setup for an increasingly 
 unpleasant situation. Here's the dialog as it happened.
 
 A: Ok, then how do I say the common operation "I want to overwrite 
 whatever the array had with 1, 2, 3"? I can only presume there must be 
 an obvious and simple way to do so, and I thought a = [1, 2, 3] was the 
 obvious syntax to achieve that.
 
 W: No, you must write
 
 a[] = [1, 2, 3];
That matches my expectation
 A: But that only works if the container already had length 3. So what I 
 need to do is this:
 
 a.length = 3;
 a[] = [1, 2, 3];
 
 A: But that is inefficient if the array had length less than 3 because 
 it means double assignment
The optimizer should be able to make that efficient.
Oct 15 2009
prev sibling next sibling parent Jeremie Pelletier <jeremiep gmail.com> writes:
Andrei Alexandrescu wrote:
 I talked to Walter about T[new] today and it seems we are having a 
 disagreement.
 
 The problem is that I believe T[new] is a container, whereas Walter 
 believes T[new] is nothing but a slice with a couple of extra operations.
I agree with the container model, it should work the way std.array.Appender does right now. T[new] would be used when you need to grow the array, and t[] when the size is final.
 Paradoxically this seems to be conducive to subtle efficiency issues. 
 For example, consider:
 
 int[new] a;
 ...
 a = [1, 2, 3];
 
 What should that do?
 
 Walter: T[new] is a slice with benefits, assignment for slices rebinds 
 the slice, therefore the assignment must do the same. In this case, the 
 assignments allocate a new array and make a refer to that array. 
 Whatever old array a referred to will continue to live wherever it was.
 
 Me: T[new] is a container, therefore the assignment must resize the 
 container from whatever size it had to 3 and then write 1, 2, 3 to its 
 three slots.
I think Walter wants to keep the syntax consistent with slices, even if T[new] is a container, it would just mean "assign this new slice to the container".
 I guess each of us has a point, but this is a setup for an increasingly 
 unpleasant situation. Here's the dialog as it happened.
 
 A: Ok, then how do I say the common operation "I want to overwrite 
 whatever the array had with 1, 2, 3"? I can only presume there must be 
 an obvious and simple way to do so, and I thought a = [1, 2, 3] was the 
 obvious syntax to achieve that.
 
 W: No, you must write
 
 a[] = [1, 2, 3];
 
 A: But that only works if the container already had length 3. So what I 
 need to do is this:
 
 a.length = 3;
 a[] = [1, 2, 3];
 
 A: But that is inefficient if the array had length less than 3 because 
 it means double assignment
If the array is of type T[new] then the runtime implementation (_d_array_new_assign?) would take care of resizing the array. As opposed to slices which can't add/remove memory from arrays anymore. You therefore wouldn't need to set the length before assignments.
 W: Nobody complained about it with slices.
 
 A: So if I do want something that does the obvious operation "Whatever 
 that array had, make it now have 1, 2, 3 as it contents" at a reasonable 
 cost I need to call an elaborate function that is highly nontrivial to 
 write?
 
 W: Looks like so.
Why would you need a nontrivial function? It's all hidden in the compiler runtime implementation, which would just get uninitialized memory if needed and then copy the assignment, its fairly simple.

 assignment to arrays. Call the assign() function"?
 
 W: Nobody complained about it with slices.
 
 ===============
 
 This goes into something more interesting that I thought of after the 
 conversation. Consider:
 
 T[new] a;
 T[] b;
 ...
 a = b;
 
 What should that do?
Raise a compiler error, its unsafe because its unknown of b is an entire array (so .ptr is a valid GC root) or a slice. T[new] would implicitly cast to T[] but the opposite requires a cast. T[new] a; T[] b; a = cast(T[new])b; // safe because we assume b.ptr is a valid gc root a = b.dup; // safe because the compiler can infer its a gc root Jeremie
Oct 15 2009
prev sibling next sibling parent dsimcha <dsimcha yahoo.com> writes:
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 I talked to Walter about T[new] today and it seems we are having a
 disagreement.
 The problem is that I believe T[new] is a container, whereas Walter
 believes T[new] is nothing but a slice with a couple of extra operations.
 Paradoxically this seems to be conducive to subtle efficiency issues.
 For example, consider:
 int[new] a;
 ...
 a = [1, 2, 3];
 What should that do?
 Walter: T[new] is a slice with benefits, assignment for slices rebinds
 the slice, therefore the assignment must do the same. In this case, the
 assignments allocate a new array and make a refer to that array.
 Whatever old array a referred to will continue to live wherever it was.
But isn't part of the point of T[new] that you're supposed to only have one T[new] pointing to any given block of memory? If you can bind a slice to a T[new], then you can bind a slice to multiple T[new]s. Then you get back to having weird bugs like: immutable(int)[new] foo; foreach(i; 0..5) { foo ~= i; } immutable(int)[] bar = foo[0..4]; immutable(int)[new] baz = bar; // References the same memory as foo baz ~= 666; writeln(foo); // prints [1 2 3 4 666]. The only way around this would be something like COW semantics, i.e. you can assign a slice to a T[new] w/o copying, but when you try to do anything with it that couldn't be done w/ a slice, then it copies.
Oct 15 2009
prev sibling next sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
Just to understand it:

int[new] a;
int[new] b;

a = [1,2,3];
b = a;

In your book, the last statement would copy contents of a into b and b.ptr 
!= a.ptr while according to walter, b would rebind to a?
Oct 16 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Lutger wrote:
 Just to understand it:
 
 int[new] a;
 int[new] b;
 
 a = [1,2,3];
 b = a;
 
 In your book, the last statement would copy contents of a into b and b.ptr 
 != a.ptr while according to walter, b would rebind to a?
Well no. In the case above b would rebind to a, which is consistent with: int[new] a = [1, 2, 3]; void fun(int[new] b) { ... } fun(a); // does not copy a I am not concerned about assigning one array to another. I'm more concerned about assigning an array literal to an array. Andrei
Oct 16 2009
parent "Denis Koroskin" <2korden gmail.com> writes:
On Fri, 16 Oct 2009 18:30:24 +0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Lutger wrote:
 Just to understand it:
  int[new] a;
 int[new] b;
  a = [1,2,3];
 b = a;
  In your book, the last statement would copy contents of a into b and  
 b.ptr != a.ptr while according to walter, b would rebind to a?
Well no. In the case above b would rebind to a, which is consistent with: int[new] a = [1, 2, 3]; void fun(int[new] b) { ... } fun(a); // does not copy a I am not concerned about assigning one array to another. I'm more concerned about assigning an array literal to an array. Andrei
So, the real question is, what's the type of an array literal? If it's T[new] then you must reassign a reference to be consistent: int[new] a = arrayLiteral; int[new] b = a; // reassign reference foo(arrayLiteral); // a reference passed Else, invoke opAssign. And its semantics is not clear. I'm in favor of removing hidden allocations and making compile-time literals immutable: immutable(int)[] a1 = [1, 2, 3]; In case of compile-time literal assignment you can't just re-assign a reference (because it's immutable), so opAssign should take care of it: int[new] a2; a2 = [1, 2, 3]; // rewritten as a2.opAssign([1, 2, 3]); Since there should be now hidden allocation, values should be overwritten. And there are two options: 1) Strict assignment: 2) Auto-expand (but not shrink!): Why not shrink? Because I believe the following test should be valid: a2 = [1, 2, 3]; assert(a2 == [1, 2, 3]); // they must match if the statement above succeeds
Oct 16 2009
prev sibling next sibling parent reply Don <nospam nospam.com> writes:
Andrei Alexandrescu wrote:
 I talked to Walter about T[new] today and it seems we are having a 
 disagreement.
 
 The problem is that I believe T[new] is a container, whereas Walter 
 believes T[new] is nothing but a slice with a couple of extra operations.
 
 Paradoxically this seems to be conducive to subtle efficiency issues. 
 For example, consider:
 
 int[new] a;
 ...
 a = [1, 2, 3];
 
 What should that do?
If we made array literals immutable, it'd be obvious. There are two sensible options: (1) An error. a = [1, 2, 3].dup; should have the semantics Walter describes. (2) Be equivalent to a.length = 3; a[] = [1,2,3]; (Andrei semantics) But in case (2), char[new] x = "abc"; should also compile (without a .dup). But I don't understand how the whole thing works. int[new] a = [1,2,3,4].dup; int[] b = a[0..3]; a.length = 1; int c = b[2]; How can this be legal in Safe D ? Without reference counting, the only option I can think of is to make it illegal to reduce the length of a T[new] array: you need to reallocate if you want to shrink it.
Oct 16 2009
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Don wrote:
 There are two sensible options:
I see the question as, is T[new] a value type or a reference type? I see it as a reference type, and so assignment should act like a reference assignment, not a value assignment.
Oct 16 2009
next sibling parent Fawzi Mohamed <fmohamed mac.com> writes:
On 2009-10-16 11:49:12 +0200, Walter Bright <newshound1 digitalmars.com> said:

 Don wrote:
 There are two sensible options:
I see the question as, is T[new] a value type or a reference type? I see it as a reference type, and so assignment should act like a reference assignment, not a value assignment.
I also see T[new] as a reference type. Unfortunately this precludes some optimizations like putting size and capacity directly at the start of the allocated array, avoiding a redirection to access data: The caller object would miss the changes if the block is reallocated. T[new] a; T[new] b; b.length=LongLength; a.length=? But as putting them at the start of the array might have an adverse effect on optimizations that expect special alignment (vector operations) maybe not everything is bad. Fawzi
Oct 16 2009
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 Don wrote:
 There are two sensible options:
I see the question as, is T[new] a value type or a reference type? I see it as a reference type, and so assignment should act like a reference assignment, not a value assignment.
I understand that, but to me that's an example of the good intentions that pave the way to hell. All of a sudden we have the best syntax there is either being surreptitiously inefficient, or not work at all. Why not see arrays as what they really are? They are a struct with a pointer inside it. The struct has opAssign. Period. Why "see" the arrays in a way that's ungainful? Andrei
Oct 16 2009
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 16 Oct 2009 05:49:12 -0400, Walter Bright  
<newshound1 digitalmars.com> wrote:

 Don wrote:
 There are two sensible options:
I see the question as, is T[new] a value type or a reference type? I see it as a reference type, and so assignment should act like a reference assignment, not a value assignment.
Andrei says you think arrays are like slices with some extra functionality, but slices are *not* a reference type, they are hybrids. Do you think T[new] arrays should be fully reference types? (I do) Otherwise, if you keep the "length is a value type" semantic, you get the same crappy appending behavior we have now. -Steve
Oct 20 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Fri, 16 Oct 2009 05:49:12 -0400, Walter Bright 
 <newshound1 digitalmars.com> wrote:
 
 Don wrote:
 There are two sensible options:
I see the question as, is T[new] a value type or a reference type? I see it as a reference type, and so assignment should act like a reference assignment, not a value assignment.
Andrei says you think arrays are like slices with some extra functionality, but slices are *not* a reference type, they are hybrids. Do you think T[new] arrays should be fully reference types? (I do)
I might have misrepresented his position. We both think T[new] is a reference type, and it was implemented that way in the now defunct feature. Andrei
Oct 20 2009
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 20 Oct 2009 09:09:46 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Fri, 16 Oct 2009 05:49:12 -0400, Walter Bright  
 <newshound1 digitalmars.com> wrote:

 Don wrote:
 There are two sensible options:
I see the question as, is T[new] a value type or a reference type? I see it as a reference type, and so assignment should act like a reference assignment, not a value assignment.
Andrei says you think arrays are like slices with some extra functionality, but slices are *not* a reference type, they are hybrids. Do you think T[new] arrays should be fully reference types? (I do)
I might have misrepresented his position. We both think T[new] is a reference type, and it was implemented that way in the now defunct feature.
Yeah, I haven't looked at the newsgroup since Thursday, and I had 500 new messages to read. Sorry for responding to this dead thread :) -Steve
Oct 20 2009
prev sibling next sibling parent reply Max Samukha <spambox d-coding.com> writes:
On Thu, 15 Oct 2009 21:55:07 -0500, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:

I talked to Walter about T[new] today and it seems we are having a 
disagreement.
I'd prefer Walter's way with a provision that array literals are immutable and allocated statically: immutable(int)[] a = [1, 2, 3]; // no allocation here int[new] b = [1, 2]; // new storage is allocated and the literal is copied there int[] a = [1, 2, 3]; // error. dup needed auto c = [1, 2, 3]; // c is of type immutable(int)[] b[] = c; //b's length is changed and c's contents copied to b's storage auto d = [1, 2, 3].dup; // d is of type int[new] auto e = [1, 2, 3].idup; // e is of type immutable(int)[new] // arrays are true reference types int[new] a = [1, 2, 3]; b = a; a.length = 22; assert (a.length == b.length);
Oct 16 2009
parent reply Don <nospam nospam.com> writes:
Max Samukha wrote:
 On Thu, 15 Oct 2009 21:55:07 -0500, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 
 I talked to Walter about T[new] today and it seems we are having a 
 disagreement.
I'd prefer Walter's way with a provision that array literals are immutable and allocated statically: immutable(int)[] a = [1, 2, 3]; // no allocation here int[new] b = [1, 2]; // new storage is allocated and the literal is copied there int[] a = [1, 2, 3]; // error. dup needed auto c = [1, 2, 3]; // c is of type immutable(int)[] b[] = c; //b's length is changed and c's contents copied to b's storage auto d = [1, 2, 3].dup; // d is of type int[new] auto e = [1, 2, 3].idup; // e is of type immutable(int)[new] // arrays are true reference types int[new] a = [1, 2, 3]; b = a; a.length = 22; assert (a.length == b.length);
This makes perfect sense to me. The rule would be: If 'x' is T[new], then: x = y; _always_ copies y into a {length, capacity-specified block}, unless it already is one. x is given a pointer to the start of that block. x[] = y[]; does a memcpy, regardless of whether y is a T[new] or a T[].
Oct 16 2009
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Don wrote:
 Max Samukha wrote:
 // arrays are true reference types
 int[new] a = [1, 2, 3];
 b = a;
 a.length = 22;
 assert (a.length == b.length);
This makes perfect sense to me. The rule would be: If 'x' is T[new], then: x = y; _always_ copies y into a {length, capacity-specified block}, unless it already is one. x is given a pointer to the start of that block. x[] = y[]; does a memcpy, regardless of whether y is a T[new] or a T[].
Right. Assignment of a reference type does not copy the values of what is referred to. Only the reference is copied. I think it would be very strange to have T[] behave like a reference type (which it does now) and T[new] to behave like a value type.
Oct 16 2009
next sibling parent reply Max Samukha <spambox d-coding.com> writes:
On Fri, 16 Oct 2009 02:53:20 -0700, Walter Bright
<newshound1 digitalmars.com> wrote:

Don wrote:
 Max Samukha wrote:
 // arrays are true reference types
 int[new] a = [1, 2, 3];
 b = a;
 a.length = 22;
 assert (a.length == b.length);
This makes perfect sense to me. The rule would be: If 'x' is T[new], then: x = y; _always_ copies y into a {length, capacity-specified block}, unless it already is one. x is given a pointer to the start of that block. x[] = y[]; does a memcpy, regardless of whether y is a T[new] or a T[].
Right. Assignment of a reference type does not copy the values of what is referred to. Only the reference is copied. I think it would be very strange to have T[] behave like a reference type (which it does now) and T[new] to behave like a value type.
By "true reference type", I meant: struct ArrayRef { Array* ptr; } struct Array { void* data; size_t length; // size_t capacity; } The fat reference approach for arrays has sucked from day one (IMHO). I think performance-critical code will use slices (read - ranges) anyway, so the additional allocation and indirection is not a big issue.
Oct 16 2009
parent reply Fawzi Mohamed <fmohamed mac.com> writes:
On 2009-10-16 13:54:03 +0200, Max Samukha <spambox d-coding.com> said:

 On Fri, 16 Oct 2009 02:53:20 -0700, Walter Bright
 <newshound1 digitalmars.com> wrote:
 
 Don wrote:
 Max Samukha wrote:
 // arrays are true reference types
 int[new] a = [1, 2, 3];
 b = a;
 a.length = 22;
 assert (a.length == b.length);
This makes perfect sense to me. The rule would be: If 'x' is T[new], then: x = y; _always_ copies y into a {length, capacity-specified block}, unless it already is one. x is given a pointer to the start of that block. x[] = y[]; does a memcpy, regardless of whether y is a T[new] or a T[].
Right. Assignment of a reference type does not copy the values of what is referred to. Only the reference is copied. I think it would be very strange to have T[] behave like a reference type (which it does now) and T[new] to behave like a value type.
By "true reference type", I meant: struct ArrayRef { Array* ptr; } struct Array { void* data; size_t length; // size_t capacity; } The fat reference approach for arrays has sucked from day one (IMHO). I think performance-critical code will use slices (read - ranges) anyway, so the additional allocation and indirection is not a big issue.
Yes exactly his is the most logically pleasing handling of resizable array, a resizable array is a ArrayRef (and the capacity there should not be commented out). Obviously this has a cost (extra indirection to access data). Fawzi
Oct 16 2009
parent reply Don <nospam nospam.com> writes:
Fawzi Mohamed wrote:
 On 2009-10-16 13:54:03 +0200, Max Samukha <spambox d-coding.com> said:
 
 On Fri, 16 Oct 2009 02:53:20 -0700, Walter Bright
 <newshound1 digitalmars.com> wrote:

 Don wrote:
 Max Samukha wrote:
 // arrays are true reference types
 int[new] a = [1, 2, 3];
 b = a;
 a.length = 22;
 assert (a.length == b.length);
This makes perfect sense to me. The rule would be: If 'x' is T[new], then: x = y; _always_ copies y into a {length, capacity-specified block}, unless it already is one. x is given a pointer to the start of that block. x[] = y[]; does a memcpy, regardless of whether y is a T[new] or a T[].
Right. Assignment of a reference type does not copy the values of what is referred to. Only the reference is copied. I think it would be very strange to have T[] behave like a reference type (which it does now) and T[new] to behave like a value type.
By "true reference type", I meant: struct ArrayRef { Array* ptr; } struct Array { void* data; size_t length; // size_t capacity; } The fat reference approach for arrays has sucked from day one (IMHO). I think performance-critical code will use slices (read - ranges) anyway, so the additional allocation and indirection is not a big issue.
Yes exactly his is the most logically pleasing handling of resizable array, a resizable array is a ArrayRef (and the capacity there should not be commented out). Obviously this has a cost (extra indirection to access data). Fawzi
Yes, but you could allocate the data immediately after the Array structure, so you only have one allocation. And in the common case, where it never exceeds the original capacity, they stay together and preserve cache locality. void *data; // = &raw_data; size_t length; size_t capacity; // = 512 ubyte[512] raw_data;
Oct 16 2009
parent reply Max Samukha <spambox d-coding.com> writes:
On Fri, 16 Oct 2009 14:25:44 +0200, Don <nospam nospam.com> wrote:

Yes, but you could allocate the data immediately after the Array 
structure, so you only have one allocation. And in the common case, 
where it never exceeds the original capacity, they stay together and 
preserve cache locality.

  void *data;   // = &raw_data;
  size_t length;
  size_t capacity; // = 512
  ubyte[512] raw_data;
Great! And if the length later exceeds the capacity, try to reallocate in place. If impossible, allocate a new block, copy the data, adjust the data pointer and shrink the original block to the size of the Array struct. Right?
Oct 16 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Max Samukha wrote:
 On Fri, 16 Oct 2009 14:25:44 +0200, Don <nospam nospam.com> wrote:
 
 Yes, but you could allocate the data immediately after the Array 
 structure, so you only have one allocation. And in the common case, 
 where it never exceeds the original capacity, they stay together and 
 preserve cache locality.

  void *data;   // = &raw_data;
  size_t length;
  size_t capacity; // = 512
  ubyte[512] raw_data;
Great! And if the length later exceeds the capacity, try to reallocate in place. If impossible, allocate a new block, copy the data, adjust the data pointer and shrink the original block to the size of the Array struct. Right?
That's the idea. The only problem that Walter pointed out was that moving GCs may have a problem with internal pointers. Andrei
Oct 16 2009
parent Leandro Lucarella <llucax gmail.com> writes:
Andrei Alexandrescu, el 16 de octubre a las 09:12 me escribiste:
 Max Samukha wrote:
On Fri, 16 Oct 2009 14:25:44 +0200, Don <nospam nospam.com> wrote:

Yes, but you could allocate the data immediately after the Array
structure, so you only have one allocation. And in the common
case, where it never exceeds the original capacity, they stay
together and preserve cache locality.

 void *data;   // = &raw_data;
 size_t length;
 size_t capacity; // = 512
 ubyte[512] raw_data;
Great! And if the length later exceeds the capacity, try to reallocate in place. If impossible, allocate a new block, copy the data, adjust the data pointer and shrink the original block to the size of the Array struct. Right?
That's the idea. The only problem that Walter pointed out was that moving GCs may have a problem with internal pointers.
GC have to support internal pointers anyways, I don't see how this changes anything... -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- We're rotten fruit We're damaged goods What the hell, we've got nothing more to lose One gust and we will probably crumble We're backdrifters
Oct 16 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 I think it would be very strange to have T[] behave like a reference 
 type (which it does now) and T[new] to behave like a value type.
T[] is not a reference type. Andrei
Oct 16 2009
parent Jason House <jason.james.house gmail.com> writes:
Andrei Alexandrescu Wrote:

 Walter Bright wrote:
 I think it would be very strange to have T[] behave like a reference 
 type (which it does now) and T[new] to behave like a value type.
T[] is not a reference type. Andrei
While true, normal use will be the same as if it was a reference type. All legal changes to a slice impact all copies of the slice.
Oct 16 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 Max Samukha wrote:
 On Thu, 15 Oct 2009 21:55:07 -0500, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 I talked to Walter about T[new] today and it seems we are having a 
 disagreement.
I'd prefer Walter's way with a provision that array literals are immutable and allocated statically: immutable(int)[] a = [1, 2, 3]; // no allocation here int[new] b = [1, 2]; // new storage is allocated and the literal is copied there int[] a = [1, 2, 3]; // error. dup needed auto c = [1, 2, 3]; // c is of type immutable(int)[] b[] = c; //b's length is changed and c's contents copied to b's storage auto d = [1, 2, 3].dup; // d is of type int[new] auto e = [1, 2, 3].idup; // e is of type immutable(int)[new] // arrays are true reference types int[new] a = [1, 2, 3]; b = a; a.length = 22; assert (a.length == b.length);
This makes perfect sense to me. The rule would be: If 'x' is T[new], then: x = y; _always_ copies y into a {length, capacity-specified block}, unless it already is one. x is given a pointer to the start of that block. x[] = y[]; does a memcpy, regardless of whether y is a T[new] or a T[].
It makes sense to make array literals immutable. The trouble is you won't be able to create arrays of most interesting types that way. class Widget { ... } Widget w1, w2, w3; auto arr = [ w1, w2, w3 ]; // error! Walter brought up the same argument at some point. He compared array literals with string literals. No! String literals only contain statically-known characters. Array literals may contain anything. Andrei
Oct 16 2009
next sibling parent reply Don <nospam nospam.com> writes:
Andrei Alexandrescu wrote:
 Don wrote:
 Max Samukha wrote:
 On Thu, 15 Oct 2009 21:55:07 -0500, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 I talked to Walter about T[new] today and it seems we are having a 
 disagreement.
I'd prefer Walter's way with a provision that array literals are immutable and allocated statically: immutable(int)[] a = [1, 2, 3]; // no allocation here int[new] b = [1, 2]; // new storage is allocated and the literal is copied there int[] a = [1, 2, 3]; // error. dup needed auto c = [1, 2, 3]; // c is of type immutable(int)[] b[] = c; //b's length is changed and c's contents copied to b's storage auto d = [1, 2, 3].dup; // d is of type int[new] auto e = [1, 2, 3].idup; // e is of type immutable(int)[new] // arrays are true reference types int[new] a = [1, 2, 3]; b = a; a.length = 22; assert (a.length == b.length);
This makes perfect sense to me. The rule would be: If 'x' is T[new], then: x = y; _always_ copies y into a {length, capacity-specified block}, unless it already is one. x is given a pointer to the start of that block. x[] = y[]; does a memcpy, regardless of whether y is a T[new] or a T[].
It makes sense to make array literals immutable. The trouble is you won't be able to create arrays of most interesting types that way. class Widget { ... } Widget w1, w2, w3; auto arr = [ w1, w2, w3 ]; // error! Walter brought up the same argument at some point. He compared array literals with string literals. No! String literals only contain statically-known characters. Array literals may contain anything. Andrei
But you can simply define: T[] makeArray(T)(T[] vars...) { return vars; } auto arr = makeArray(w1, w2, w3); Unless we make arrays immutable, I don't know how we can define an array of compile-time constants.
Oct 16 2009
parent reply Don <nospam nospam.com> writes:
Don wrote:
 Andrei Alexandrescu wrote:
 Don wrote:
 Max Samukha wrote:
 On Thu, 15 Oct 2009 21:55:07 -0500, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 I talked to Walter about T[new] today and it seems we are having a 
 disagreement.
I'd prefer Walter's way with a provision that array literals are immutable and allocated statically: immutable(int)[] a = [1, 2, 3]; // no allocation here int[new] b = [1, 2]; // new storage is allocated and the literal is copied there int[] a = [1, 2, 3]; // error. dup needed auto c = [1, 2, 3]; // c is of type immutable(int)[] b[] = c; //b's length is changed and c's contents copied to b's storage auto d = [1, 2, 3].dup; // d is of type int[new] auto e = [1, 2, 3].idup; // e is of type immutable(int)[new] // arrays are true reference types int[new] a = [1, 2, 3]; b = a; a.length = 22; assert (a.length == b.length);
This makes perfect sense to me. The rule would be: If 'x' is T[new], then: x = y; _always_ copies y into a {length, capacity-specified block}, unless it already is one. x is given a pointer to the start of that block. x[] = y[]; does a memcpy, regardless of whether y is a T[new] or a T[].
It makes sense to make array literals immutable. The trouble is you won't be able to create arrays of most interesting types that way. class Widget { ... } Widget w1, w2, w3; auto arr = [ w1, w2, w3 ]; // error! Walter brought up the same argument at some point. He compared array literals with string literals. No! String literals only contain statically-known characters. Array literals may contain anything. Andrei
But you can simply define: T[] makeArray(T)(T[] vars...) { return vars; } auto arr = makeArray(w1, w2, w3); Unless we make arrays immutable, I don't know how we can define an array of compile-time constants.
In case this isn't clear: real [] sinsTable = [ sin(1.0), sin(2.0), sin(3.0), sin(4.0) ]; How do you do this so that the entries in the table are calculated at compile time?
Oct 16 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 In case this isn't clear:
 
 real [] sinsTable = [ sin(1.0), sin(2.0), sin(3.0), sin(4.0) ];
 
 How do you do this so that the entries in the table are calculated at 
 compile time?
static? Andrei
Oct 16 2009
parent Don <nospam nospam.com> writes:
Andrei Alexandrescu wrote:
 Don wrote:
 In case this isn't clear:

 real [] sinsTable = [ sin(1.0), sin(2.0), sin(3.0), sin(4.0) ];

 How do you do this so that the entries in the table are calculated at 
 compile time?
static? Andrei
That's still not compile time. They're initialized in the module constructor. If you make them an enum array, they're at compile time, but then you shouldn't be able to index the array at runtime (the whole point of 'enum' was that it doesn't get stored).
Oct 16 2009
prev sibling parent Max Samukha <spambox d-coding.com> writes:
On Fri, 16 Oct 2009 09:00:27 -0500, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:

Don wrote:
 Max Samukha wrote:
 On Thu, 15 Oct 2009 21:55:07 -0500, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 I talked to Walter about T[new] today and it seems we are having a 
 disagreement.
I'd prefer Walter's way with a provision that array literals are immutable and allocated statically: immutable(int)[] a = [1, 2, 3]; // no allocation here int[new] b = [1, 2]; // new storage is allocated and the literal is copied there int[] a = [1, 2, 3]; // error. dup needed auto c = [1, 2, 3]; // c is of type immutable(int)[] b[] = c; //b's length is changed and c's contents copied to b's storage auto d = [1, 2, 3].dup; // d is of type int[new] auto e = [1, 2, 3].idup; // e is of type immutable(int)[new] // arrays are true reference types int[new] a = [1, 2, 3]; b = a; a.length = 22; assert (a.length == b.length);
This makes perfect sense to me. The rule would be: If 'x' is T[new], then: x = y; _always_ copies y into a {length, capacity-specified block}, unless it already is one. x is given a pointer to the start of that block. x[] = y[]; does a memcpy, regardless of whether y is a T[new] or a T[].
It makes sense to make array literals immutable. The trouble is you won't be able to create arrays of most interesting types that way. class Widget { ... } Widget w1, w2, w3; auto arr = [ w1, w2, w3 ]; // error! Walter brought up the same argument at some point. He compared array literals with string literals. No! String literals only contain statically-known characters. Array literals may contain anything. Andrei
Ok. But it is unacceptable to allocate the literal on heap if all its elements are statically known. I'd rather have a library function than the current situation. Why not: auto arr = array(w1, w2, w3)? This is an important issue. Please don't leave it unaddressed.
Oct 16 2009
prev sibling next sibling parent Sergey Gromov <snake.scaly gmail.com> writes:
Thu, 15 Oct 2009 21:55:07 -0500, Andrei Alexandrescu wrote:

 int[new] a;
 ...
 a = [1, 2, 3];
 
 What should that do?
To me a is an array, a reference type. Therefore assignment here means rebinding a to a new array created from a literal.
 A: Ok, then how do I say the common operation "I want to overwrite 
 whatever the array had with 1, 2, 3"? I can only presume there must be 
 an obvious and simple way to do so, and I thought a = [1, 2, 3] was the 
 obvious syntax to achieve that.
I'd say a.replace([1, 2, 3]);
 T[new] a;
 T[] b;
 ...
 a = b;
 
 What should that do?
Error: type mismatch. Use a = b.dup;
Oct 17 2009
prev sibling parent Kagamin <spam here.lot> writes:
Andrei Alexandrescu Wrote:

 Paradoxically this seems to be conducive to subtle efficiency issues. 
 For example, consider:
 
 int[new] a;
 ...
 a = [1, 2, 3];
 
 What should that do?
a = new Appender!int([1,2,3]); What you describe is more like StringBuilder, and, yes, things like that require full-blown API.
Oct 19 2009