June 04, 2007
Oskar Linde 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:
> 
> An excellent suggestion. And I'm not only saying that because I have suggested this several times myself. :p

Can you point me to the posting(s)? You should get the credit for being first.

>>
>>    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 like "resizeable" array because it is pretty clear what it does.

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

Looks like C99's VLA.

> 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. :)

Frits has posted a realistic use case that argues it shouldn't be.
June 04, 2007
Walter Bright wrote:
> 
> (The problem is if I pass a slice to a function, and then that function reallocates the slice by changing the length or appending to it, it can affect other slices into the same data in very hard-to-explain ways.

I'm not sure I understand how a reallocation could affect other slices into the same data.  If a reallocation occurs, that reallocation will be into entirely new memory, provided the slice doesn't start at the head of a memory block.  Is it the condition I just mentioned that's a problem?  If so, I suppose it is one way to trick the compiler into thinking the reference no longer refers to const data, when in fact it does.  If not, could you point me in the right direction?

> 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

Well, I think it is very rare for a function to want to resize an array that isn't passed by reference.  My first reaction would be to disallow resizing of non-ref array parameters entirely.  Since resizing may occur in place, this could be seen as a mutating operation on the underlying data.  If the user wants to resize a non-ref array parameter for local use he use assign ops and such.

I think this may be related to a question I've been avoiding until now: how will the new const features interact with struct and class member function use?  Will it always be legal to call member functions of const objects?  Will it never be legal?  Or perhaps there will be a way to label some operations as non-modifying (which suggests logical const)?


Sean
June 04, 2007
Walter Bright wrote:
> Derek Parnell wrote:
>> On Mon, 04 Jun 2007 02:33:58 -0700, Walter Bright wrote:
>>>     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.
> 

so with T[n], .length (all by it's self) is a compile time const, with T[] it is an r-value and with T[new] it is an l-value. Is that more or less the sum of it?
June 04, 2007
Walter Bright skrev:
> Oskar Linde 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:
>>
>> An excellent suggestion. And I'm not only saying that because I have suggested this several times myself. :p
> 
> Can you point me to the posting(s)? You should get the credit for being first.

Looking back at some of my posts, I don't think I ever made a public newsgroup post actually suggesting D change in this way... For the reason that I never dared to believe such a change could ever happen. I found some rants on the topic of the separate semantics of slices and resizable arrays, but it is hardly posts I feel very proud of. :p

http://www.digitalmars.com/d/archives/digitalmars/D/bugs/bug_or_feature_9420.html#N9425
http://www.digitalmars.com/d/archives/digitalmars/D/learn/3521.html#N3593
http://www.digitalmars.com/d/archives/digitalmars/D/learn/3354.html#N3357

>>>    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 like "resizeable" array because it is pretty clear what it does. 

Ok, another suggestion: (Sneakily injecting the [*] too... :)

T[n]   a;  // static array
T[]    b;  // array view
T[*]   c;  // dynamic array

IMHO, "array view" is a better description than "dynamic array". But it depends on your point of view, of course.

>> I also agree with others that there are better alternatives to "new". T[*] is my favorite.
> 
> Looks like C99's VLA.

Afaik, it is only used for vla _prototypes_ in C99, and is that really a problem? It is not like the compiler won't warn you if you write:

extern(C) void foo(int,int[*]);

right? And I don't really see how using T[*] for resizable arrays in D is any different from using T[] for non-resizable ones -- T[] also means something slightly different in C99.

>> 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. :)
> 
> Frits has posted a realistic use case that argues it shouldn't be.

I presume you mean:

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)."""

The buffer.length = <something>; in the use case might be a neat trick, but is it really that essential? Consider the rewrite: (T[] means non-resizable dynamic array)

T[] foo(<parameters>, T[] buffer = null)
{
    T[] ret = buffer.length >= needed_capacity
            ? buffer[0..needed_capacity]
            : new T[needed_capacity];
    <fill ret>
    return ret;
}

The only advantage of the original code is that it might be able to squeeze a few extra bytes out of a buffer that for some reason would not be at its full capacity. But is that really a feature? I would personally not have expected this assert to potentially fail:

T[] buffer = new T[1000];
T[] ret = foo(buffer);
assert(ret.length <= buffer.length || ret.ptr != buffer.ptr);

So I don't really see what you gain from being able to pass a resizable array by value and then change its length. At least not compared to what you gain by not being able to do that.

/Oskar
June 04, 2007
Sean Kelly wrote:
> Walter Bright wrote:
>> (The problem is if I pass a slice to a function, and then that function reallocates the slice by changing the length or appending to it, it can affect other slices into the same data in very hard-to-explain ways.
> 
> I'm not sure I understand how a reallocation could affect other slices into the same data.  If a reallocation occurs, that reallocation will be into entirely new memory, provided the slice doesn't start at the head of a memory block.  Is it the condition I just mentioned that's a problem?  If so, I suppose it is one way to trick the compiler into thinking the reference no longer refers to const data, when in fact it does.  If not, could you point me in the right direction?

Given:

	int[] a = new int[7];
	int[] b = a[1..6];
	b[1] = 2;   // writes through to a[2]
	b.length = 4;
	b[1] = 3;   // writes through to a[2]
	b.length = 6;
	b[1] = 4;   // does not write through to a[2]

>> 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
> 
> Well, I think it is very rare for a function to want to resize an array that isn't passed by reference.  My first reaction would be to disallow resizing of non-ref array parameters entirely.  Since resizing may occur in place, this could be seen as a mutating operation on the underlying data.  If the user wants to resize a non-ref array parameter for local use he use assign ops and such.

That was my first reaction too, but Frits posted a reasonable use case.


> I think this may be related to a question I've been avoiding until now: how will the new const features interact with struct and class member function use?  Will it always be legal to call member functions of const objects?  Will it never be legal?  Or perhaps there will be a way to label some operations as non-modifying (which suggests logical const)?

You'll only be able to call const member functions on const objects.
June 04, 2007
BCS wrote:
> Walter Bright wrote:
>> Derek Parnell wrote:
>>> On Mon, 04 Jun 2007 02:33:58 -0700, Walter Bright wrote:
>>>>     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.
>>
> 
> so with T[n], .length (all by it's self) is a compile time const, with T[] it is an r-value and with T[new] it is an l-value. Is that more or less the sum of it?

Yes.
June 04, 2007
"Daniel Keep" <daniel.keep.lists@gmail.com> wrote in message news:f412os$24fh$1@digitalmars.com...
>
> Another thing to consider is that T[*] requires your hands to move further than either T[$] or T[..].

Ho-hoh!  I've got an asterisk key between my left ctrl and alt keys.  No shift for me ;)

> T[.]

Odd.  Doesn't say much.

> T[,]

Looks like a 2D rectangular array (like in C#).

> T[`]
> T[-]
> T[=]
> T[;]

All odd.  T[=] doesn't look bad, T[-] is easier to type for me.


June 04, 2007
Walter Bright wrote:
> Sean Kelly wrote:
>> Walter Bright wrote:
>>> (The problem is if I pass a slice to a function, and then that function reallocates the slice by changing the length or appending to it, it can affect other slices into the same data in very hard-to-explain ways.
>>
>> I'm not sure I understand how a reallocation could affect other slices into the same data.  If a reallocation occurs, that reallocation will be into entirely new memory, provided the slice doesn't start at the head of a memory block.  Is it the condition I just mentioned that's a problem?  If so, I suppose it is one way to trick the compiler into thinking the reference no longer refers to const data, when in fact it does.  If not, could you point me in the right direction?
> 
> Given:
> 
>     int[] a = new int[7];
>     int[] b = a[1..6];
>     b[1] = 2;   // writes through to a[2]
>     b.length = 4;
>     b[1] = 3;   // writes through to a[2]
>     b.length = 6;
>     b[1] = 4;   // does not write through to a[2]

Okay, so it is related to non-reallocating changes.  Thanks.

>>> 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
>>
>> Well, I think it is very rare for a function to want to resize an array that isn't passed by reference.  My first reaction would be to disallow resizing of non-ref array parameters entirely.  Since resizing may occur in place, this could be seen as a mutating operation on the underlying data.  If the user wants to resize a non-ref array parameter for local use he use assign ops and such.
> 
> That was my first reaction too, but Frits posted a reasonable use case.

Do you mean:

    T[] foo(<parameters>, T[] buffer = null) {
        buffer.length = <something>;
        <fill buffer>
        return buffer;
    }

Wouldn't it work if changed to this:

    T[] foo(<parameters>, T[] buffer = null) {
        if( buffer.length < newSize )
            buffer = new T[newSize];
        <fill buffer>
        return buffer;
    }

I think it's arguable that the second form is also more meaningful given that the array is not passed by reference.


Sean
June 04, 2007
Oskar Linde wrote:
> The buffer.length = <something>; in the use case might be a neat trick, but is it really that essential? Consider the rewrite: (T[] means non-resizable dynamic array)
> 
> T[] foo(<parameters>, T[] buffer = null)
> {
>     T[] ret = buffer.length >= needed_capacity
>             ? buffer[0..needed_capacity]
>             : new T[needed_capacity];
>     <fill ret>
>     return ret;
> }

You're probably right with that.
June 04, 2007
On Mon, 04 Jun 2007 10:45:09 -0700, Walter Bright wrote:

>>>
>>>    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 like "resizeable" array because it is pretty clear what it does.

I think you are quite wrong Walter. The new interpretation of 'x[]' is almost identical to what we used to think of as a slice. And the interpreatation of 'x[new]' is exactly what used to be known as a dynamic array.

Your change will effect all code immediately whereas reversing the (changed) meaning of '[]' and '[new]' to mean 'resizable' and 'slice' will allow coders to change their coding over time. Unless of course that's what you are trying to do ...


-- 
Derek Parnell
Melbourne, Australia
"Justice for David Hicks!"
skype: derek.j.parnell