www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Is alias equality inconsistent?

reply Jens Mueller <jens.k.mueller gmx.de> writes:
Hi,

I find that comparing for alias equality is a bit odd. Maybe somebody
can shed some light into it.

int a = 1;
int b = 1;
assert(a is b);

but

int[1] aFixedArray = [1];
int[1] bFixedArray = [1];
assert(aFixedArray !is bFixedArray);

It behaves as specified in TDPL p. 57:
"If a and b are arrays or class references, the result is true if and
only if a and b are two names for the same actual object;
Otherwise, a is b is the same as a == b."

But fixed-size arrays have value semantics as int, float etc. So why is
alias equality not consistent with value vs. reference semantics? Why
this exception for fixed-size arrays?

Jens
May 13 2011
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
 Hi,

 I find that comparing for alias equality is a bit odd. Maybe somebody
 can shed some light into it.

 int a = 1;
 int b = 1;
 assert(a is b);

 but

 int[1] aFixedArray = [1];
 int[1] bFixedArray = [1];
 assert(aFixedArray !is bFixedArray);

 It behaves as specified in TDPL p. 57:
 "If a and b are arrays or class references, the result is true if and
 only if a and b are two names for the same actual object;
 Otherwise, a is b is the same as a == b."

 But fixed-size arrays have value semantics as int, float etc. So why is
 alias equality not consistent with value vs. reference semantics? Why
 this exception for fixed-size arrays?

 Jens
Hi, I think it is more consistent this way, because static arrays are closer to dynamic arrays than to int, float etc. If you write generic code, it is more likely that the template will be instantiated with for example both of int[6] and int[] than int[6] and float for the same parameter. You really want the semantics of 'is' to be consistent between different array types. Timon
May 13 2011
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-05-13 12:30, Timon Gehr wrote:
 Hi,
 
 I find that comparing for alias equality is a bit odd. Maybe somebody
 can shed some light into it.
 
 int a = 1;
 int b = 1;
 assert(a is b);
 
 but
 
 int[1] aFixedArray = [1];
 int[1] bFixedArray = [1];
 assert(aFixedArray !is bFixedArray);
 
 It behaves as specified in TDPL p. 57:
 "If a and b are arrays or class references, the result is true if and
 only if a and b are two names for the same actual object;
 Otherwise, a is b is the same as a == b."
 
 But fixed-size arrays have value semantics as int, float etc. So why is
 alias equality not consistent with value vs. reference semantics? Why
 this exception for fixed-size arrays?
 
 Jens
Hi, I think it is more consistent this way, because static arrays are closer to dynamic arrays than to int, float etc. If you write generic code, it is more likely that the template will be instantiated with for example both of int[6] and int[] than int[6] and float for the same parameter. You really want the semantics of 'is' to be consistent between different array types.
Except that static arrays and dynamic arrays are different beasts and should _not_ be treated the same. A dynamic array which references a static array should be treated the same as another dynamic array, but static arrays and dynamic arrays should _not_ be treated the same. They're very different. - Jonathan M Davis
May 13 2011
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
Jonathan M Davis wrote:
  On 2011-05-13 12:30, Timon Gehr wrote:
 Hi,

 I find that comparing for alias equality is a bit odd. Maybe somebody
 can shed some light into it.

 int a = 1;
 int b = 1;
 assert(a is b);

 but

 int[1] aFixedArray = [1];
 int[1] bFixedArray = [1];
 assert(aFixedArray !is bFixedArray);

 It behaves as specified in TDPL p. 57:
 "If a and b are arrays or class references, the result is true if and
 only if a and b are two names for the same actual object;
 Otherwise, a is b is the same as a == b."

 But fixed-size arrays have value semantics as int, float etc. So why is
 alias equality not consistent with value vs. reference semantics? Why
 this exception for fixed-size arrays?

 Jens
Hi, I think it is more consistent this way, because static arrays are closer to dynamic arrays than to int, float etc. If you write generic code, it is more likely that the template will be instantiated with for example both of int[6] and int[] than int[6] and float for the same parameter. You really want the semantics of 'is' to be consistent between different array types.
Except that static arrays and dynamic arrays are different beasts and should _not_ be treated the same. A dynamic array which references a static array should be treated the same as another dynamic array, but static arrays and dynamic arrays should _not_ be treated the same. They're very different. - Jonathan M Davis
If that is true to the extent implied by you, then D is doing it all wrong anyway: declaration: int[] arr; vs int[N] arr; extract value: arr[x] vs arr[x] store value: arr[x] = v; vs arr[x] = v; slice: arr[a..b] vs arr[a..b] get pointer: arr.ptr vs arr.ptr get length: arr.length vs arr.length ... (you see where this is going) Jonathan M Davis wrote:
 I'm surprised that is works with value semantics at all. I would have expected
 that to be a compiler error. Barring that, I would have expected it to be
 false for value types unless a variable was compared with itself with is. This
 definitely seems off.
Not at all. What you suggest is that 'is' should compare the addresses of its arguments. It is not at all like that. For classes 'is' compares its actual arguments while == has an additional indirection. You can think of value types as references to unique immutable data. They behave just the same, except that they are more efficient. I think 'is' works just as it is supposed to. Timon
May 13 2011
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
 Jonathan M Davis wrote:
 On 2011-05-13 12:30, Timon Gehr wrote:
 Hi,
 
 I find that comparing for alias equality is a bit odd. Maybe somebody
 can shed some light into it.
 
 int a = 1;
 int b = 1;
 assert(a is b);
 
 but
 
 int[1] aFixedArray = [1];
 int[1] bFixedArray = [1];
 assert(aFixedArray !is bFixedArray);
 
 It behaves as specified in TDPL p. 57:
 "If a and b are arrays or class references, the result is true if and
 only if a and b are two names for the same actual object;
 Otherwise, a is b is the same as a == b."
 
 But fixed-size arrays have value semantics as int, float etc. So why
 is alias equality not consistent with value vs. reference semantics?
 Why this exception for fixed-size arrays?
 
 Jens
Hi, I think it is more consistent this way, because static arrays are closer to dynamic arrays than to int, float etc. If you write generic code, it is more likely that the template will be instantiated with for example both of int[6] and int[] than int[6] and float for the same parameter. You really want the semantics of 'is' to be consistent between different array types.
Except that static arrays and dynamic arrays are different beasts and should _not_ be treated the same. A dynamic array which references a static array should be treated the same as another dynamic array, but static arrays and dynamic arrays should _not_ be treated the same. They're very different. - Jonathan M Davis
If that is true to the extent implied by you, then D is doing it all wrong anyway: declaration: int[] arr; vs int[N] arr; extract value: arr[x] vs arr[x] store value: arr[x] = v; vs arr[x] = v; slice: arr[a..b] vs arr[a..b] get pointer: arr.ptr vs arr.ptr get length: arr.length vs arr.length ... (you see where this is going)
They are different because dynamic arrays are reference types and static arrays are value types. That has a definite impact when copying and assigning them. It has an impact on anything which differs depending on value and reference semantics. That can have far-reaching impacts.
 Jonathan M Davis wrote:
 I'm surprised that is works with value semantics at all. I would have
 expected that to be a compiler error. Barring that, I would have
 expected it to be false for value types unless a variable was compared
 with itself with is. This definitely seems off.
Not at all. What you suggest is that 'is' should compare the addresses of its arguments. It is not at all like that. For classes 'is' compares its actual arguments while == has an additional indirection. You can think of value types as references to unique immutable data. They behave just the same, except that they are more efficient. I think 'is' works just as it is supposed to.
It was my understanding that is was specifically intended for comparing reference equality. As such, it has no business being used to compare value types. If, on the other hand, it is merely doing a comparison with no indirections, then is and == would be the same for value types and would make sense. I'm not sure what was actually intended by Walter. I am, however, very surprised to see is work with value types. - Jonathan M Davis
May 13 2011
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
Jonathan M Davis wrote:
 If that is true to the extent implied by you, then D is doing it all wrong
 anyway:

 declaration: int[] arr; vs int[N] arr;
 extract value: arr[x] vs arr[x]
 store value: arr[x] = v; vs arr[x] = v;
 slice: arr[a..b] vs arr[a..b]
 get pointer: arr.ptr vs arr.ptr
 get length: arr.length vs arr.length
 ... (you see where this is going)
They are different because dynamic arrays are reference types and static arrays are value types. That has a definite impact when copying and assigning them. It has an impact on anything which differs depending on value and reference semantics. That can have far-reaching impacts.
I understand that. But that does not mean they should be treated differently where they can be treated uniformly. What I am claiming is that 'is' is a feature on the same level as the primitives listed above.
 Jonathan M Davis wrote:
 I'm surprised that is works with value semantics at all. I would have
 expected that to be a compiler error. Barring that, I would have
 expected it to be false for value types unless a variable was compared
 with itself with is. This definitely seems off.
Not at all. What you suggest is that 'is' should compare the addresses of its arguments. It is not at all like that. For classes 'is' compares its actual arguments while == has an additional indirection. You can think of value types as references to unique immutable data. They behave just the same, except that they are more efficient. I think 'is' works just as it is supposed to.
It was my understanding that is was specifically intended for comparing reference equality. As such, it has no business being used to compare value types. If, on the other hand, it is merely doing a comparison with no indirections, then is and == would be the same for value types and would make sense. I'm not sure what was actually intended by Walter. I am, however, very surprised to see is work with value types. - Jonathan M Davis
As its in TDPL and DMD I think that is pretty much what Walter intended. 'is' is only there because there is no other (safe/convenient) means of testing for shallow equality in D. Fixed-size arrays are somewhat special, there 'is' actually compares ptr and length. I think that is very practical and makes sense because it allows meaningful 'is' between fixed- and dynamic-size arrays. I guess for value types it is there for completeness and maybe to enable easier generic programming. We do not lose anything by having it. Timon
May 13 2011
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/13/11 5:00 PM, Timon Gehr wrote:
 Fixed-size arrays are somewhat special, there 'is' actually compares ptr and
 length. I think that is very practical and makes sense because it allows
 meaningful 'is' between fixed- and dynamic-size arrays. I guess for value
types it
 is there for completeness and maybe to enable easier generic programming. We do
 not lose anything by having it.
I think 'is' for static arrays could go either way. If they're to be seen as an equivalent of structs, is should yield true. If they're to be seen as a close relative of dynamic arrays, is should yield false. Probably it would be best to disable it :o). Andrei
May 13 2011
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
 On 5/13/11 5:00 PM, Timon Gehr wrote:
 Fixed-size arrays are somewhat special, there 'is' actually compares ptr and
 length. I think that is very practical and makes sense because it allows
 meaningful 'is' between fixed- and dynamic-size arrays. I guess for value
types it
 is there for completeness and maybe to enable easier generic programming. We do
 not lose anything by having it.
I think 'is' for static arrays could go either way. If they're to be seen as an equivalent of structs, is should yield true. If they're to be seen as a close relative of dynamic arrays, is should yield false. Probably it would be best to disable it :o). Andrei
I think determining the return value should rely on other things than the ref-ness of the arguments. Maybe you want to clarify. When you are disabling stuff, also disable templates and all builtin types. Using the mixin/asm statements they can be implemented entirely in library code. I will then start patching my compiler. I am serious about that one. =) When did it stop being useful? I thought D should be a practical language that gets you stuff done, not a religion. Now it seems to me that you want to disable 'is' for static arrays just because there would be another (less useful) way to define it? Or is the ':o)' an indication that you were just joking? It would be a relief. Timon
May 13 2011
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
 On 5/13/11 5:00 PM, Timon Gehr wrote:
 Fixed-size arrays are somewhat special, there 'is' actually compares ptr
 and length. I think that is very practical and makes sense because it
 allows meaningful 'is' between fixed- and dynamic-size arrays. I guess
 for value types it is there for completeness and maybe to enable easier
 generic programming. We do not lose anything by having it.
I think 'is' for static arrays could go either way. If they're to be seen as an equivalent of structs, is should yield true. If they're to be seen as a close relative of dynamic arrays, is should yield false. Probably it would be best to disable it :o). Andrei
I think determining the return value should rely on other things than the ref-ness of the arguments. Maybe you want to clarify. When you are disabling stuff, also disable templates and all builtin types. Using the mixin/asm statements they can be implemented entirely in library code. I will then start patching my compiler. I am serious about that one. =) When did it stop being useful? I thought D should be a practical language that gets you stuff done, not a religion. Now it seems to me that you want to disable 'is' for static arrays just because there would be another (less useful) way to define it? Or is the ':o)' an indication that you were just joking? It would be a relief.
The only value of is and static arrays that I can think of is if you can use them to verify whether a dynamic array refers to a particular static array. Other than that, what value do they add? If you're checking for equality, then use ==. That one use case may make it useful to allow for is in comparing static and dynamic arrays, but other than that, I don't really see what is gives you when comparing static arrays. - Jonathan M Davis
May 13 2011
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
 The only value of is and static arrays that I can think of is if you can use
 them to verify whether a dynamic array refers to a particular static array.
 Other than that, what value do they add? If you're checking for equality, then
 use ==. That one use case may make it useful to allow for is in comparing
 static and dynamic arrays, but other than that, I don't really see what is
 gives you when comparing static arrays.

 - Jonathan M Davis
is on static arrays compares ptr and length too. So it is different from ==.
May 13 2011
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-05-13 17:28, Timon Gehr wrote:
 The only value of is and static arrays that I can think of is if you can
 use them to verify whether a dynamic array refers to a particular static
 array. Other than that, what value do they add? If you're checking for
 equality, then use ==. That one use case may make it useful to allow for
 is in comparing static and dynamic arrays, but other than that, I don't
 really see what is gives you when comparing static arrays.
 
 - Jonathan M Davis
is on static arrays compares ptr and length too. So it is different from ==.
And of what value is that? == should already doing that before checking for equality on each of the elements, because if the ptr and length are identical, then there's no point checking the elements for equality. So, is doesn't add any benefit there, unless == is poorly implemented for static arrays. Static arrays are value types, not reference types, so checking whether two of them refer to the same array makes no sense. Checking whether a dynamic array refers to a static array makes sense, but checking whether two static arrays are the same array does not. And since == should shortcut when comparing a static array to itself (heck, the compiler should be able to optimize the check out entirely and replace it with true), I don't see how using is on two static arrays does anything for you at all. - Jonathan M Davis
May 13 2011
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
 On 2011-05-13 17:28, Timon Gehr wrote:
 The only value of is and static arrays that I can think of is if you can
 use them to verify whether a dynamic array refers to a particular static
 array. Other than that, what value do they add? If you're checking for
 equality, then use ==. That one use case may make it useful to allow for
 is in comparing static and dynamic arrays, but other than that, I don't
 really see what is gives you when comparing static arrays.

 - Jonathan M Davis
is on static arrays compares ptr and length too. So it is different from ==.
And of what value is that? == should already doing that before checking for equality on each of the elements, because if the ptr and length are identical, then there's no point checking the elements for equality. So, is doesn't add any benefit there, unless == is poorly implemented for static arrays.
== currently checks length but not ptr for both static and dynamic arrays.
 Static arrays are value types, not reference types, so checking whether two of
 them refer to the same array makes no sense. Checking whether a dynamic array
 refers to a static array makes sense, but checking whether two static arrays
 are the same array does not. And since == should shortcut when comparing a
 static array to itself (heck, the compiler should be able to optimize the
 check out entirely and replace it with true), I don't see how using is on two
 static arrays does anything for you at all.

 - Jonathan M Davis
Ah, thats what you are referring to. Yes, 'is' on two static arrays can be resolved entirely on compile time. And disallowing 'is' for static arrays is actually one check more in the compiler, so the compiler gets slightly more complicated. This works against D's goals ;). Apart from that, saying that dynamic arrays are reference types and ergo 'is' makes sense is somewhat incomplete, because they are actually value types (structs) with some reference-like behavior: //does not change its argument void foo(int[] a){ a.length=3*a.length; a[0]=10029; } //this does (but it cannot take a static array) void foo(ref int[] a){ a.length=3*a.length; a[0]=10029; } It is not beautiful but practical... Same goes for some reference-like behavior of static arrays. You can think of static arrays as being the same as dynamic arrays except that they define a postblit operator and cannot be resized. You can also think of them as being structs that embed all the data and define opBinary("is"). The current semantics of static arrays allow both interpretations if you do not want to get too low level. What about static array references by the way? With ref arguments static and dynamic arrays even have similar semantics (except that you cannot change the length of a static array). But ref arguments are a different beast. 'is' does not work as may be expected by some: bool bar(ref int a, ref int b){ return a is b; } void main(){ int x=0,y=1; assert(!bar(x,y)); x=1; assert(bar(x,y)); } Again, if you understand that value types are similar to immutable references, it makes sense. But I think that this behavior could as well be changed. Besides: consider a little analogy: if(x==y){..} //fine. if(x==1){..} //fine. if(2==y){..} //fine. if(2==1){..} //there for completeness Just because you do not want to write 2==1 so often, do you want to remove it from the language? And then you would remove x==1 and 2==y too? I think what D is doing now feels right and consistent. Timon
May 14 2011
parent Timon Gehr <timon.gehr gmx.ch> writes:
 Again, if you understand that value types are similar to immutable references,
it
 makes sense. But I think that this behavior could as well be changed.
Just realized that changing it would imply there would be no way to check for alias equality of two class reference references. It should remain as it is. Timon
May 14 2011