June 04, 2007
Bill Baxter wrote:
> T{} = new T[10];
> 
> Probably breaks context freeness or something, but I like it if for no other reason than the braces are shift-[ shift-] on the US keyboard. It's also easy to type, and it's a nice mnemonic for array with augmented capabilities.

I missed that in my original reply. {} are slightly harder to
type than [] on german keyboards, because all the braces require the use of the alt-gr key (just right of space).
This means [] is .. doable, but quickly typing {} requires quite a bit of finger stretching.
(I usually put the thumb on alt-gr and reach over it with the index digit, and .. well, { is the alt-gr usage of the 7 key. Acrobatics! :D )
 -- downs
June 04, 2007
Lionello Lunesu wrote:
> Tom S wrote:
>> Daniel Keep wrote:
>>> The "new" inside the brackets just looks strange; a new what?
>>
>> I'm with you here ;)
>>
>>
>>> A few possible alternatives:
>>>
>>> T[new]    // Proposed
>>
>> Looks awkward, yeah. And it would produce false hits for anyone doing new-hunting (happens when you care about allocations, but prototype using 'new').
> 
> Huh. To me, that sounds like an argument *for* using [new], since any array passed as [new] can cause an allocation in that function.

Not really, if you consider that it may be modified using realloc or some other wild schemes. And these are ok. I'd usually search for 'new', '~' and 'length' :P


> I like the [new] syntax :P

Nooooo! What have I done! *slaps himself with a trout*


-- 
Tomasz Stachowiak
http://h3.team0xf.com/
h3/h3r3tic on #D freenode
June 04, 2007
On Mon, 04 Jun 2007 02:33:58 -0700, Walter Bright wrote:

> ... we finally hit on the idea of making a resizeable array a different type, say:
> 
>     T[n]   a;  // static array
>     T[]    b;  // dynamic array
>     T[new] c;  // resizeable array
> 
>     a.length = 6;   // error, static array
>     b.length = 6;   // error, dynamic array is not resizeable
>     c.length = 6;   // ok

Huh? 'static array' used to mean fixed-size array and 'dynamic array' used to mean 'variable-length array' ... so a dynamic array is not dynamic anymore?

So a dynamic array is 'sizeable' but not 'resizable'. In other words, once its length has been set it can never be changed again? Is that what you are saying? And is that a compile-time or a run-time check?

If this is what 'resizeable' means for dynamic arrays then nearly all my dynamic arrays will have to be changed to resizable ones, because I rely on that feature for lots of things.

What is the point of having dynmaic arrays now? The difference between them an static arrays is rather small.

>     b = c;          // ok, implicit conversion of resizeable to dynamic

But hang on, you said that b.length isn't allowed to change? Or did you mean that it can only change if b.ptr also being updated?

> I think this will entail only very rare changes to user code, but will be a big improvement in type safety.

Not so sure about that ... I'll do some analysis and report back.

-- 
Derek Parnell
Melbourne, Australia
"Justice for David Hicks!"
skype: derek.j.parnell
June 04, 2007
Walter Bright wrote:
> Now, it turns out that it is very rare for a function to legitimately want to resize a buffer passed to it. So we finally hit on the idea of making a resizeable array a different type, say:
> 
>    T[n]   a;  // static array
>    T[]    b;  // dynamic array
>    T[new] c;  // resizeable array
> 
>    a.length = 6;   // error, static array
>    b.length = 6;   // error, dynamic array is not resizeable
>    c.length = 6;   // ok
>    b = c;          // ok, implicit conversion of resizeable to dynamic
>    c = b;          // error
>    c = b[0..n];    // error
>    b = c[0..n];    // ok, but watch out if c is later resized
>    b = c[0..n].dup; // ok, no worries
>    c = cast(T[new])b; // ok, but this will be deliberate

What about
	c = a;
?
I have several places where I have functions that follow this pattern:
---
T[] foo(<parameters>, T[] buffer = null) {
    buffer.length = <something>;
    <fill buffer>
    return buffer;
}
---
These are intended to be usable with "buffer" being either a stack-allocated static array or a heap-allocated dynamic array. If the passed buffer is big enough a slice is returned, otherwise it gets enlarged (and possibly reallocated on the heap).
I guess with this proposed modification I'd want to accept and return a "T[new]", but will that allow static arrays (stack-allocated) to be passed in?
I'd hate to have to write two different functions, one for static arrays and slices and one for resizable arrays... (and I'm not fond of the idea of extra casts to make it work with a single function)

Also, I often write toString() members of objects (and other functions returning strings) like this:
---
char[] toString() {
    auto ret = "prefix";
    foreach (elt; children) {
        ret ~= elt.toString();
        // perhaps append a separator here
    }
    // if separators were appended, remove the last one here
    ret ~= "postfix";
    return ret;
}
---
Will these need to be changed? (probably a .dup after the "prefix" string)
(Since string constants are also static arrays, this is the same basic issue as above)

> I think this will entail only very rare changes to user code, but will be a big improvement in type safety. Note that it will *still* possible for array resizing to affect other slices into the same array, but it's far, far less likely to happen inadvertently. The attractiveness of this solution is it nearly completely solves the problem with zero runtime cost.

I think I may need to make quite some changes actually, though the changes required to the latter code sample I gave will be minimal.


Another question, will the following be allowed:
---
class Base {
    abstract char[] foo();
}

class Derived : Base {
    override char[new] foo() { assert(0, "Not implemented yet"); }
}
---
?
June 04, 2007
Bill Baxter wrote:
> Daniel Keep wrote:
>> T[*]	// Indicating the index could be anything
> 
> That's not bad.

It means something completely different in C99.  Not that that matters much.


> T[~] // mnemonic: T can be concatenated onto
> 

Somehow this appeals to me. :)
June 04, 2007

Daniel Keep wrote:
> ...
> A few possible alternatives:
> 
> T[new]	// Proposed
> T[$]	// Since $ already means "length" for arrays
> T[*]	// Indicating the index could be anything
> T[..]	// Bit of a stretch...
> T[volatile] // Bit long
> T[[]]	// Just ugly

One thing that didn't occur to me until just now is that T[$], T[*], T[~] (and someone suggested T[?] on IRC) require you to alternate the use of the shift key.  This means that I keep accidentally typing T[$} or T{$] or T[4}.

I do think that T[*] is probably the nicest looking without giving the wrong initial impression (T[$] could be misinterpreted as a typo, as could T[..]), but is tricky to type quickly.  T[..] is easy to type fast, as is T[new].

Another thing to consider is that T[*] requires your hands to move further than either T[$] or T[..].

A few other possible symbols that can be entered without using shift (at
least on my keyboard):

T[.]
T[,]
T[`]
T[-]
T[=]
T[;]

I'm not counting keys on the numeric keypad because it's too far away from the usual resting position of the hands.

Just something to chew on :)

	-- Daniel
June 04, 2007
Walter Bright wrote:

> Now, it turns out that it is very rare for a function to legitimately want to resize a buffer passed to it. So we finally hit on the idea of making a resizeable array a different type, say:

An excellent suggestion. And I'm not only saying that because I have suggested this several times myself. :p

The dual resizable array / slice semantics of the old dynamic array have several subtle problems, and also considering the fact that slices and resizable arrays are two very different beasts that seldom need to be mixed makes this a welcome change. I'm glad that introducing separate types seems to be relatively painless.

> 
>    T[n]   a;  // static array
>    T[]    b;  // dynamic array
>    T[new] c;  // resizeable array

I'd propose a different nomenclature:

T[n]   a;  // static array
T[]    b;  // (array) slice
T[new] c;  // dynamic array

I also agree with others that there are better alternatives to "new". T[*] is my favorite.

> So, if our function parameter is typed as T[], we know there isn't going to be any monkey business going on in that function with our other slices. If it's T[new], we know we need to take a hard look at it.

It will still be a bit weird appending to or resizing a T[new] function argument that isn't passed by reference... Actually it will most likely be a bug. It would be *very* neat getting that fixed too.

In 99.9 cases of 100 where one passes a T[new] as a function argument, one wants the argument to be passed by reference. The remaining 0.1 cases have obvious workarounds. So... Is there any way T[new] somehow could be made a reference type? Just like T[U] has become.

E.g. it would be nice if the following just worked:

void appendTo(T[new] arr, T val) { arr ~= val; }

Without the problem of the user accidentally forgetting to pass arr by reference.

Short version: You always want to pass ref T[new]. Forgetting ref is probably a bug, but is silently accepted by the compiler. Ergo, ref should be the default. :)

/Oskar
June 04, 2007
Tom S wrote:
>> A few possible alternatives:
>>
>> T[new]    // Proposed
> 
> Looks awkward, yeah. And it would produce false hits for anyone doing new-hunting (happens when you care about allocations, but prototype using 'new').

I'm not necessarily sold on that syntax, but it's workable and unambiguous.

> 
>> T[$]    // Since $ already means "length" for arrays
> 
> I don't like it... Looks like an out of bounds error to my parser ;)

There are parsing ambiguities with that I wish to avoid.


>> T[*]    // Indicating the index could be anything
> ++votes;

That is used for VLA's in C99, which could mislead people.
June 04, 2007
Derek Parnell wrote:
> On Mon, 04 Jun 2007 02:33:58 -0700, Walter Bright wrote:
> 
>> ... we finally hit on the idea of making a resizeable array a different type, say:
>>
>>     T[n]   a;  // static array
>>     T[]    b;  // dynamic array
>>     T[new] c;  // resizeable array
>>
>>     a.length = 6;   // error, static array
>>     b.length = 6;   // error, dynamic array is not resizeable
>>     c.length = 6;   // ok
> 
> Huh? 'static array' used to mean fixed-size array

It still does.

> and 'dynamic array' used
> to mean 'variable-length array' ... so a dynamic array is not dynamic
> anymore?

A dynamic array would now mean that its length is not known at compile time.

> So a dynamic array is 'sizeable' but not 'resizable'. In other words, once
> its length has been set it can never be changed again? Is that what you are
> saying?

Yes.

> And is that a compile-time or a run-time check?

Compile time.

> If this is what 'resizeable' means for dynamic arrays then nearly all my
> dynamic arrays will have to be changed to resizable ones, because I rely on
> that feature for lots of things. 

Without looking at your code, I suspect it may be less than you think. I thought I was modifying strings all the time, so when switching over to const strings, I expected lots and lots of work. Turns out, only rarely do I do this.


> What is the point of having dynmaic arrays now? The difference between them
> an static arrays is rather small.

Consider the following:

	alias char[] string;

That alone justifies it! And the difference between static arrays and dynamic arrays is very large. For one thing, static arrays are passed by value, and dynamic arrays by reference.

>>     b = c;          // ok, implicit conversion of resizeable to dynamic
> 
> But hang on, you said that b.length isn't allowed to change? Or did you
> mean that it can only change if b.ptr also being updated?

I meant that the underlying array cannot be resized through b. b itself can have its ptr/length values changed through simple assignment, but there will be no realloc happening.

>> I think this will entail only very rare changes to user code, but will be a big improvement in type safety. 
> 
> Not so sure about that ... I'll do some analysis and report back.
> 
June 04, 2007
Frits van Bommel wrote:
> Walter Bright wrote:
>> Now, it turns out that it is very rare for a function to legitimately want to resize a buffer passed to it. So we finally hit on the idea of making a resizeable array a different type, say:
>>
>>    T[n]   a;  // static array
>>    T[]    b;  // dynamic array
>>    T[new] c;  // resizeable array
>>
>>    a.length = 6;   // error, static array
>>    b.length = 6;   // error, dynamic array is not resizeable
>>    c.length = 6;   // ok
>>    b = c;          // ok, implicit conversion of resizeable to dynamic
>>    c = b;          // error
>>    c = b[0..n];    // error
>>    b = c[0..n];    // ok, but watch out if c is later resized
>>    b = c[0..n].dup; // ok, no worries
>>    c = cast(T[new])b; // ok, but this will be deliberate
> 
> What about
>     c = a;
> ?

Good question. That'll be allowed. I have a couple misgivings about it, though.

> I have several places where I have functions that follow this pattern:
> ---
> T[] foo(<parameters>, T[] buffer = null) {
>     buffer.length = <something>;
>     <fill buffer>
>     return buffer;
> }
> ---
> These are intended to be usable with "buffer" being either a stack-allocated static array or a heap-allocated dynamic array. If the passed buffer is big enough a slice is returned, otherwise it gets enlarged (and possibly reallocated on the heap).
> I guess with this proposed modification I'd want to accept and return a "T[new]", but will that allow static arrays (stack-allocated) to be passed in?

Yes.

> I'd hate to have to write two different functions, one for static arrays and slices and one for resizable arrays... (and I'm not fond of the idea of extra casts to make it work with a single function)
> 
> Also, I often write toString() members of objects (and other functions returning strings) like this:
> ---
> char[] toString() {
>     auto ret = "prefix";
>     foreach (elt; children) {
>         ret ~= elt.toString();
>         // perhaps append a separator here
>     }
>     // if separators were appended, remove the last one here
>     ret ~= "postfix";
>     return ret;
> }
> ---
> Will these need to be changed? (probably a .dup after the "prefix" string)
> (Since string constants are also static arrays, this is the same basic issue as above)

You'd need to rewrite it as:

---
string toString() {
    invariant(char)[new] ret = "prefix";
    foreach (elt; children) {
        ret ~= elt.toString();
        // perhaps append a separator here
    }
    // if separators were appended, remove the last one here
    ret ~= "postfix";
    return ret;
}
---
since the type of "prefix" will be invariant(char)[6];
You'll need to use "string" as a return type, because you'll potentially be returning a reference to an immutable string literal, and that must be reflected in the type. If you chose to do "prefix".dup, it could be written as:

---
string toString() {
    auto ret = "prefix".dup;
    foreach (elt; children) {
        ret ~= elt.toString();
        // perhaps append a separator here
    }
    // if separators were appended, remove the last one here
    ret ~= "postfix";
    return ret;
}
---

and the return type could be either string or char[]. The type of ret here will be char[new], as that is what .dup will return.

>> I think this will entail only very rare changes to user code, but will be a big improvement in type safety. Note that it will *still* possible for array resizing to affect other slices into the same array, but it's far, far less likely to happen inadvertently. The attractiveness of this solution is it nearly completely solves the problem with zero runtime cost.
> 
> I think I may need to make quite some changes actually, though the changes required to the latter code sample I gave will be minimal.
> 
> 
> Another question, will the following be allowed:
> ---
> class Base {
>     abstract char[] foo();
> }
> 
> class Derived : Base {
>     override char[new] foo() { assert(0, "Not implemented yet"); }
> }
> ---
> ?

Yes, since char[new] can be implicitly converted to char[]. The reverse will not work.