June 28, 2004
On Sun, 27 Jun 2004 22:55:46 +0000 (UTC), Farmer wrote:

> Sean Kelly <sean@f4.ca> wrote in news:cbn29h$rpo$1@digitaldaemon.com:
> 
>> Not really.  I'd rather argue that D tries to make both usable and reduce odd errors resulting from uninitialized arrays.
> 
> I think, D tries to *hide* errors resulting from uninitialized arrays.
> 
>> 
>>>This is unfortunate as
>>>
>>>1) a clear separation of empty-arrays vs. null-arrays is useful for functional rich but simple API interfaces:
>>>
>>>Imagine a function that returns the value of attributes of a XML-element char[] getAttrValue(char[] name)
>>>
>>>The attribute value could be non-existant (the attribute doesn't exist), be empty, or have a non-empty value.
>> 
>> I'd say this is an interface or documentaation problem, not a language problem.
> 
> You misunderstood me, I meant that the function interface is a good one.
> I could document the function like this:
> /*
>    Function returns the value the attribute of the given name.
>    @param  name  name of the attribute
>    @return  returns null if the attribute doesn't exist
>             returns value of the attribute otherwise
> */
> char[] getAttrValue(char[] name)
> 
> But the other functions, I mentioned would be a necessary workaround if you couldn't distinguish between null and empty arrays. And these functions are a waste of both cpu cycles and developer brain.
> 
>>>2) Initialization bugs are not detected at runtime.
>> 
>> This makes sense in this case.  I don't like the idea of having to distinguish between an initialized array with no elements and an uninitialized array, as both are equivalent IMO.  Further, setting the length property will cause a reallocation for both types of arrays.
> 
> Well, it's quite easy to do distinquish between an empty and a null array: An uninitialized array (null array) is a bug in either the programmer's code or in the code of a library. An initialized array (empty array) is a perfectly legal thing.


Well....the *use* of an uninitialized array it which it is assumed to be initialized is a bug. The fact, or presence, of an uninitialized array is itself is not really a bug. Also, the use of an empty array may well be a bug in other circumstances, even though is it 'a legal thing'.

> Why is the idea to distinguish between a bug and correct programm behaviour such an unpleasent thing?

It's not, and no one said it was. We are talking about distinguishing between an array that has not been set to anything specific *yet*, and one that has been set explictly though assignment, to contain zero elements.

There is a timing issue here. For example, it might be prudent in some situations to only initialize an array if its actually going to be used. This is a run-time decision and not a compile time decision.



-- 
Derek
Melbourne, Australia
28/Jun/04 10:44:13 AM
June 28, 2004
On Sun, 27 Jun 2004 17:02:27 -0700, Andy Friesen wrote:

> Farmer wrote:
> 
>> Andy Friesen <andy@ikagames.com> wrote in news:cbn3js$tgq$1@digitaldaemon.com:
>> 
>>>I think the problem is that D arrays almost always behave like reference types, and therefore are almost always treated like reference types.
>> 
>> Yes, this is a problem. It is a necessary evil to archive that outstanding performance. But it is not really related to the topic null array vs. empty array, since empty arrays are possible with the D array layout
> Sure, in the same sense that D allows 'empty' integers. :)
>
>>>They aren't.  null arrays *are* empty arrays.
>> 
>> No, null arrays are not empty arrays, as my sample proofs.
> Conceptually they are.  If the length is zero, then the data pointer is meaningless.  Testing the data pointer in such a case can be likened to using the result of a division by zero.  Doing things like mathematically 'proving' that 3==5 or that empty!==null is easy when you go into the twilight zone. :)

Huh? There are times when a zero-length array is valid and an uninitalized array is not valid. There are simply not the same thing.

  if (a === null)
    { // Initialize it }
  else
    { if (a.length == 0)
      {
        // Empty situation. I DO NOT WANT TO INITIALIZE IT HERE!
      }
      else
      {
        // Use the non-empty array
      }
    }


> As an example:
> 
>      import std.string;
> 
>      char[] permute(char[] c) {
>          // mutate that to which the array refers
>          c[0] = 'H';
>          // mutate the array
>          c.length = 4;
>          return c;
>      }
> 
>      int main() {
>          char[] c = "hello world!";
>          printf("%s\n", toStringz(c));
> 
>          char[] d = permute(c);
> 
>          printf("Post-permute\n");
>          printf("%s\n", toStringz(c));
>          printf("%s\n", toStringz(d));
>          return 0;
>      }
> 
> This program produces the output:
> 
> 	hello world!
> 	Hello world!
> 	Hell
> 
> The array is a value type.  The data it points to is not.
> 
>>>Arrays are value types which consist of a length and a pointer to memory.  Copying and slicing an array creates a brand new array whose data happens to (generally) be memory that is also pointed to by another array.
>> 
>> I think there's a lapsus, slices *always* point to the same memory as the array from which they were created.
> In my experience, this is true, but I don't know if it *must*, so I felt obligated to qualify my statement.

Yes, it could be an artifact of the D compiler rather than the D language.

-- 
Derek
Melbourne, Australia
28/Jun/04 10:51:51 AM
June 28, 2004
On Sun, 27 Jun 2004 17:02:27 -0700, Andy Friesen <andy@ikagames.com> wrote:
> Farmer wrote:
>
>> Andy Friesen <andy@ikagames.com> wrote in news:cbn3js$tgq$1@digitaldaemon.com:
>>
>>> I think the problem is that D arrays almost always behave like reference types, and therefore are almost always treated like reference types.
>>
>> Yes, this is a problem. It is a necessary evil to archive that outstanding  performance. But it is not really related to the topic null array vs. empty array, since empty arrays are possible with the D array layout

> Sure, in the same sense that D allows 'empty' integers. :)

D allows both empty arrays *and* null arrays.
It does *not* allow both empty *and* null integers.
They are different and not comparable.

>>> They aren't.  null arrays *are* empty arrays.
>>
>> No, null arrays are not empty arrays, as my sample proofs.

> Conceptually they are. If the length is zero, then the data pointer is meaningless.

I disagree. Conceptually they aren't the same, as both my example and 'Farmers' have proven for the case of a char array. Even with other array types there is still a conceptual difference between an array that does not exist and one containing no elements. In a large number of real world cases you would treat the 2 the same, but that does not make them the same, and is no reason to preclude the ability to treat them differently.

Even in D's implementation they aren't exactly the same, consider:

0) char[] a;
1) char[] b = "regan";
2) b = "";
3) b = null;

at 0 a's data pointer is null and length is zero
at 1 b's data pointer is non-null and length is 5
at 2 b's data pointer is non-null and length is 0

I am not 100% certain what happens at 3, either:
  at 3 b's data pointer is null and length is 0
or
  at 3 b's data pointer is non-null and length is 0

in either case 'a' (the null array) is not the same as 'b' when it is an empty array, and may not be even when 'b' is a null array.

> Testing the data pointer in such a case can be likened to using the result of a division by zero. Doing things like mathematically 'proving' that 3==5 or that empty!==null is easy when you go into the twilight zone. :)

> As an example:
>
>      import std.string;
>
>      char[] permute(char[] c) {
>          // mutate that to which the array refers
>          c[0] = 'H';
>          // mutate the array
>          c.length = 4;
>          return c;
>      }
>
>      int main() {
>          char[] c = "hello world!";
>          printf("%s\n", toStringz(c));
>
>          char[] d = permute(c);
>
>          printf("Post-permute\n");
>          printf("%s\n", toStringz(c));
>          printf("%s\n", toStringz(d));
>          return 0;
>      }
>
> This program produces the output:
>
> 	hello world!
> 	Hello world!
> 	Hell
>
> The array is a value type.  The data it points to is not.
>
>>> Arrays are value types which consist of a length and a pointer to memory.  Copying and slicing an array creates a brand new array whose data happens to (generally) be memory that is also pointed to by another array.
>>
>> I think there's a lapsus, slices *always* point to the same memory as the array from which they were created.
> In my experience, this is true, but I don't know if it *must*, so I felt obligated to qualify my statement.

The simple fact remains that we require both null strings (and possibly other arrays) and empty strings and that conceptually they are different, or rather they can mean different things and/or demand different behaviour.

All I'm advocating is that test for null to not compare true for an empty array, and thus a null array and an empty array not to compare equal.

Regan.

-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
June 28, 2004
Derek Parnell" <derek@psych.ward> wrote:
> There is a timing issue here. For example, it might be prudent in some situations to only initialize an array if its actually going to be used. This is a run-time decision and not a compile time decision.

What I do to handle such issues is to check the array length only. See, even if the array is unallocated the length is still valid (because arrays are a pointer/length pair). If the length is zero, you move on. If not, then the pointer *should* be valid. That is, a length-check can perform double duty. For example:

void foo (char[] bar)
{
    if (bar.length)
        // do something
        ;
}

main ()
{
    foo (null);
}

- Kris


June 28, 2004
On Sun, 27 Jun 2004 18:09:05 -0700, Kris <someidiot@earthlink.dot.dot.dot.net> wrote:
> Derek Parnell" <derek@psych.ward> wrote:
>> There is a timing issue here. For example, it might be prudent in some
>> situations to only initialize an array if its actually going to be used.
>> This is a run-time decision and not a compile time decision.
>
> What I do to handle such issues is to check the array length only. See, even
> if the array is unallocated the length is still valid (because arrays are a
> pointer/length pair). If the length is zero, you move on. If not, then the
> pointer *should* be valid. That is, a length-check can perform double duty.
> For example:
>
> void foo (char[] bar)
> {
>     if (bar.length)
>         // do something
>         ;
> }
>
> main ()
> {
>     foo (null);
> }

I think Derek is thinking more of this other example he gave:

  if (a === null)
    { // Initialize it }
  else
    { if (a.length == 0)
      {
        // Empty situation. I DO NOT WANT TO INITIALIZE IT HERE!
      }
      else
      {
        // Use the non-empty array
      }
    }

The array above is initialized if it's null. Otherwise it is handled based on whether it has items in it.

We need to be able to tell the difference between empty and null, and it needs to be consistent. The inconsistencies as I see them are:

empty array == null        //true
empry array == null array  //true

whereas both should be false.

No change needs to be made to the way the length property works, as you say it's useful if you do not need to handle them differently.

Regan.

-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
June 28, 2004
Regan Heath wrote:
>>> Yes, this is a problem. It is a necessary evil to archive that outstanding  performance. But it is not really related to the topic null array vs. empty array, since empty arrays are possible with the D array layout
> 
>> Sure, in the same sense that D allows 'empty' integers. :)
> 
> D allows both empty arrays *and* null arrays.
> It does *not* allow both empty *and* null integers.
> They are different and not comparable.
> 

D arrays are implement exactly so:

	struct Array {
	    int length;
	    void* data;
	}

	Array a; // value type
	int i; // value type

'i' will never be null, and 'a' never will either, because both types exist on the stack.  'a' can be *compared* to null because an implicit pointer conversion is performed.  However, if 'a' does not contain any data, its pointer value is meaningless, so the result of such a comparison is undefined.  Either way, 'a' itself is *not* null any more than 'i' ever could be.

(I'm not saying that this is how it should be, I'm just saying that this is how it is)

>>>> They aren't.  null arrays *are* empty arrays.
>>>
>>> No, null arrays are not empty arrays, as my sample proofs.
> 
>> Conceptually they are. If the length is zero, then the data pointer is meaningless.
> 
> I disagree. Conceptually they aren't the same, as both my example and 'Farmers' have proven for the case of a char array. Even with other array types there is still a conceptual difference between an array that does not exist and one containing no elements. In a large number of real world cases you would treat the 2 the same, but that does not make them the same, and is no reason to preclude the ability to treat them differently.

This goes back to D performing implicit pointer conversion.  Comparing arrays with null is not a good idea.

> The simple fact remains that we require both null strings (and possibly other arrays) and empty strings and that conceptually they are different, or rather they can mean different things and/or demand different behaviour.
> 
> All I'm advocating is that test for null to not compare true for an empty array, and thus a null array and an empty array not to compare equal.

I'm still forming an opinion on whether this is the right thing to do or not.  If comparing arrays with pointers was illegal, this issue would never arise.

As for testing existence against emptiness, I suggest you do the same thing you would for an integer (or any other value type) for which nil and zero/empty/T.init must be distinguishable.

 -- andy
June 28, 2004
On Sun, 27 Jun 2004 19:15:10 -0700, Andy Friesen <andy@ikagames.com> wrote:
> Regan Heath wrote:
>>>> Yes, this is a problem. It is a necessary evil to archive that outstanding  performance. But it is not really related to the topic null array vs. empty array, since empty arrays are possible with the D array layout
>>
>>> Sure, in the same sense that D allows 'empty' integers. :)
>>
>> D allows both empty arrays *and* null arrays.
>> It does *not* allow both empty *and* null integers.
>> They are different and not comparable.
>>
>
> D arrays are implement exactly so:
>
> 	struct Array {
> 	    int length;
> 	    void* data;
> 	}
>
> 	Array a; // value type
> 	int i; // value type
>
> 'i' will never be null, and 'a' never will either, because both types exist on the stack.  'a' can be *compared* to null because an implicit pointer conversion is performed.  However, if 'a' does not contain any data, its pointer value is meaningless, so the result of such a comparison is undefined.  Either way, 'a' itself is *not* null any more than 'i' ever could be.
>
> (I'm not saying that this is how it should be, I'm just saying that this is how it is)

I see what you're saying... the internal data pointer for the array can be null or non-null however, this is the difference between an un-initialized (or null) array and an empty one.

I dont care how we do it, I just know we need to be able to tell the difference for 'strings'. Perhaps this applies to all arrays. Perhaps strings need to be a specialized form of array...

>>>>> They aren't.  null arrays *are* empty arrays.
>>>>
>>>> No, null arrays are not empty arrays, as my sample proofs.
>>
>>> Conceptually they are. If the length is zero, then the data pointer is meaningless.
>>
>> I disagree. Conceptually they aren't the same, as both my example and 'Farmers' have proven for the case of a char array. Even with other array types there is still a conceptual difference between an array that does not exist and one containing no elements. In a large number of real world cases you would treat the 2 the same, but that does not make them the same, and is no reason to preclude the ability to treat them differently.
>
> This goes back to D performing implicit pointer conversion.  Comparing arrays with null is not a good idea.

Perhaps not, but, there is currently no other way to tell the difference between an empty string and a null string. This is very important.

>> The simple fact remains that we require both null strings (and possibly other arrays) and empty strings and that conceptually they are different, or rather they can mean different things and/or demand different behaviour.
>>
>> All I'm advocating is that test for null to not compare true for an empty array, and thus a null array and an empty array not to compare equal.
>
> I'm still forming an opinion on whether this is the right thing to do or not.  If comparing arrays with pointers was illegal, this issue would never arise.

True, but then you wouldn't be able to tell null strings from empty ones.

> As for testing existence against emptiness, I suggest you do the same thing you would for an integer (or any other value type) for which nil and zero/empty/T.init must be distinguishable.

I suspect an arrays .init parameter *is* null. in which case

  uint[] c;
  if (c == c.init)

is equvalent to

  if (c == null)


I was just recently told by Walter not to use the init value of an array.
I was trying to re-init the array, i.e.

uint[4] c = [0,1,2,3];

c = c.init
c[] = c.init;
c[] = c[].init;

none of those work. Walters soln...

static uint[4] cinit = [0,1,2,3];
uint[4] c;

c[] = cinit[];


Why can't .init do this implicitly? For my original example it would create one static array, and my array called 'c' then set c.init to the static array, so that

c = c.init;

would work. For an array that is not initialized c.init can stay null as

c = c.init;

would then be equivalent to

c = null;


Regan.

-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
June 28, 2004
In article <opr99w0st25a2sq9@digitalmars.com>, Regan Heath says...
>> (1) given that a is an array of length n, the expression a[n..n] gives
>> an array
>> bounds exception,

>This (now?) works.

Indeed, I think it has always worked. It was just me misremembering the problem. I'll start again. What I MEANT was...

Given that a is an array of length n, the expression &a[n] gives an array bounds exception. And I don't believe it should. Taking the address of the first byte beyond the end of an array can be a very useful thing to do.

In particular, if a is an empty array, then &a[0] asserts, which means that code like this:

#    // given ubyte[] a;
#    fread(&a[0], ubyte.size, a.length, fp);

intended to fill an array from a FILE*-type stream, will fall over if a is empty. And there's no reason why it should - fread is quite happy to be passed a length of zero. Same goes for functions like memset() and so on.

The fact of not being able to take &a[a.length] creates an akwardness that we have to code around. The above example would have to be encased in an if test in order not to assert - and you might think: So what? This is no big deal. But having to make that explicit test time and time again can start to get annoying.

It should not, in my opinion, be an error to evaluate &a[a.length];

Arcane Jill


June 28, 2004
Farmer <itsFarmer.@freenet.de> wrote in news:Xns951699362221itsFarmer@63.105.9.61:

> Arcane Jill <Arcane_member@pathlink.com> wrote in news:cbn5da$vu1$1@digitaldaemon.com:
> 
>> In article <Xns9515C8A3CA1ACitsFarmer@63.105.9.61>, Farmer says...

>> (2) I think it is wrong that the test (a == null) will return true if and only if BOTH the length AND the address are zero. I think, if we're going to have a model in which the statement a = null; will create an empty array, then (a == null) should return true if a /is/ an empty array. That is, only the length should be tested, not the address. (If you want to test both parts, well there's always a === null).
> 
> I guess the rule here is simple: For value types (as the array handle is
> one) ==/equals() is exactly the same as ===/is.

My mistake, forget about this sentence, it is utter rubbish:
For primitive value types,  ===/is  behaves like  ==/equals() , rather than
the other way round. Furthermore, array handles aren't primitive types.
June 28, 2004
Regan Heath <regan@netwin.co.nz> wrote in
news:opr99w0st25a2sq9@digitalmars.com:
[snip]

> I think the problem with arrays is that a null array should not compare
> equal to an empty array. In other words the original post test(s)
>    null1 == ""
>    null1 == empty1
> 
> should be false.
> 

Exactly, otherwise the equals() method would not be transitive. (Of course, we could also make  (empty1 == null)  evaluate to true by completely banning empty-arrays from the D sphere.)


Regards,
   Farmer.