Re: putting more smarts into a == b

2009-09-27 Thread Frank Benoit
Andrei Alexandrescu schrieb:
 Consider two objects a and b with a of class type. Currently, the
 expression a == b is blindly rewritten as a.opEquals(b). I argue it
 should be rewritten into a call to an (imaginary/inlined) function
 equalObjects(a, b), with the following definition:
 
 bool equalObjects(T, U)(T a, U b) if (is(T == class))
 {
 static if (is(U == class))
 {
 if (b is null) return a is null;
 if (a is null) return b is null;
 }
 else
 {
 enforce(a !is null);
 }
 return a.opEquals(b);
 }
 
 This hoists the identity test outside the opEquals call and also deals
 with null references. What do you think?
 
 
 Andrei

What about interfaces?


Re: putting more smarts into a == b

2009-09-27 Thread Frank Benoit
Frank Benoit schrieb:
 What about interfaces?

I mean, this is a point that annoyes me a lot in D, that interfaces
(instances) cannot be treated like objects. I cannot do

if( someiface == someobj ){ ... }

With that technique, the compiler could do a dynamic cast to Object in
place, do the null checks and then call opEquals.

Certainly, this should also work for the other methods of Objects like
toHash, toString, ...


Re: putting more smarts into a == b

2009-09-27 Thread Christopher Wright

Jarrett Billingsley wrote:

On Sat, Sep 26, 2009 at 9:32 PM, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:

Consider two objects a and b with a of class type. Currently, the expression
a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten
into a call to an (imaginary/inlined) function equalObjects(a, b), with the
following definition:

bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
   static if (is(U == class))
   {
   if (b is null) return a is null;
   if (a is null) return b is null;
   }
   else
   {
   enforce(a !is null);
   }
   return a.opEquals(b);
}

This hoists the identity test outside the opEquals call and also deals with
null references. What do you think?


I'm almost sure that C# does this already, and it's a useful behavior.


C# operator overloads are of the form:
public static ReturnType operator+(Arg1 arg1, Arg2 arg2) {}

Object.operator== is defined to call arg1.Equals(arg2) if arg1 isn't 
null. But this isn't a feature of operator overloads.



Of course, with nonnull types, the check for null wouldn't even need
to exst ;)


How clever and insightful of you!


Re: putting more smarts into a == b

2009-09-27 Thread Andrei Alexandrescu

Robert Jacques wrote:
On Sat, 26 Sep 2009 21:32:13 -0400, Andrei Alexandrescu 
seewebsiteforem...@erdani.org wrote:


Consider two objects a and b with a of class type. Currently, the 
expression a == b is blindly rewritten as a.opEquals(b). I argue it 
should be rewritten into a call to an (imaginary/inlined) function 
equalObjects(a, b), with the following definition:


bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
 static if (is(U == class))
 {
 if (b is null) return a is null;
 if (a is null) return b is null;
 }
 else
 {
 enforce(a !is null);
 }
 return a.opEquals(b);
}

This hoists the identity test outside the opEquals call and also deals 
with null references. What do you think?



Andrei


I like this. I think optimizing away opEquals for identical objects 
would also be a good idea:


static if (is(U == class))
if(a is b || a is null || b  is null) return a is b;
else
enforce(a !is null);


This code has an inefficiency, it seems, because it makes a bit more 
checks than necessary (e.g. checks a is b twice). Let's simplify:


bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
 static if (is(U == class))
 {
 if (a is b) return true;
 if (b is null || a is null) return false;
 }
 else
 {
 enforce(a !is null);
 }
 return a.opEquals(b);
}


Andrei


Re: putting more smarts into a == b

2009-09-27 Thread Andrei Alexandrescu

Frank Benoit wrote:

Andrei Alexandrescu schrieb:

Consider two objects a and b with a of class type. Currently, the
expression a == b is blindly rewritten as a.opEquals(b). I argue it
should be rewritten into a call to an (imaginary/inlined) function
equalObjects(a, b), with the following definition:

bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
static if (is(U == class))
{
if (b is null) return a is null;
if (a is null) return b is null;
}
else
{
enforce(a !is null);
}
return a.opEquals(b);
}

This hoists the identity test outside the opEquals call and also deals
with null references. What do you think?


Andrei


What about interfaces?


Good question! What do they do now? I ran this:

interface A {}
class Widget : A {}

void main() {
auto a = cast(A) new Widget;
A b = null;
writeln(a == b);
writeln(b == a);
}

To my surprise, the program printed false twice. If I replace A with 
Widget inside main, the program prints false then crashes with the 
mythical segfault :o).


So how are interfaces compared?


Andrei


Re: putting more smarts into a == b

2009-09-27 Thread Andrei Alexandrescu

Frank Benoit wrote:

Andrei Alexandrescu schrieb:

Frank Benoit wrote:

Andrei Alexandrescu schrieb:

Consider two objects a and b with a of class type. Currently, the
expression a == b is blindly rewritten as a.opEquals(b). I argue it
should be rewritten into a call to an (imaginary/inlined) function
equalObjects(a, b), with the following definition:

bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
static if (is(U == class))
{
if (b is null) return a is null;
if (a is null) return b is null;
}
else
{
enforce(a !is null);
}
return a.opEquals(b);
}

This hoists the identity test outside the opEquals call and also deals
with null references. What do you think?


Andrei

What about interfaces?

Good question! What do they do now? I ran this:

interface A {}
class Widget : A {}

void main() {
auto a = cast(A) new Widget;
A b = null;
writeln(a == b);
writeln(b == a);
}

To my surprise, the program printed false twice. If I replace A with
Widget inside main, the program prints false then crashes with the
mythical segfault :o).

So how are interfaces compared?


Andrei


Hm, i would have expected it not to compile, because A does not have
opEquals.

In DWT, I cast always first to Object.
Java if( intf1.equals(intf2) ){
D1.0 if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){


I think in D the cast is inserted automatically. Walter?

Andrei


Re: putting more smarts into a == b

2009-09-27 Thread Steven Schveighoffer
On Sun, 27 Sep 2009 10:32:29 -0400, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:



Frank Benoit wrote:

Andrei Alexandrescu schrieb:

Frank Benoit wrote:

Andrei Alexandrescu schrieb:

Consider two objects a and b with a of class type. Currently, the
expression a == b is blindly rewritten as a.opEquals(b). I argue it
should be rewritten into a call to an (imaginary/inlined) function
equalObjects(a, b), with the following definition:

bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
static if (is(U == class))
{
if (b is null) return a is null;
if (a is null) return b is null;
}
else
{
enforce(a !is null);
}
return a.opEquals(b);
}

This hoists the identity test outside the opEquals call and also  
deals

with null references. What do you think?


Andrei

What about interfaces?

Good question! What do they do now? I ran this:

interface A {}
class Widget : A {}

void main() {
auto a = cast(A) new Widget;
A b = null;
writeln(a == b);
writeln(b == a);
}

To my surprise, the program printed false twice. If I replace A with
Widget inside main, the program prints false then crashes with the
mythical segfault :o).

So how are interfaces compared?


Andrei

 Hm, i would have expected it not to compile, because A does not have
opEquals.
 In DWT, I cast always first to Object.
Java if( intf1.equals(intf2) ){
D1.0 if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){


I think in D the cast is inserted automatically. Walter?


From the assembly, it appears that the compiler is comparing the reference  
values directly.  Which is not what you want, you want the opEquals from  
Object.


-Steve


Re: putting more smarts into a == b

2009-09-27 Thread Frank Benoit
Andrei Alexandrescu schrieb:
 Frank Benoit wrote:
 In DWT, I cast always first to Object.
 Java if( intf1.equals(intf2) ){
 D1.0 if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){
 
 I think in D the cast is inserted automatically. Walter?
 
 Andrei

there is a related bug report
http://d.puremagic.com/issues/show_bug.cgi?id=2794


Re: putting more smarts into a == b

2009-09-27 Thread Robert Jacques
On Sun, 27 Sep 2009 09:11:38 -0400, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:



Robert Jacques wrote:
On Sat, 26 Sep 2009 21:32:13 -0400, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:


Consider two objects a and b with a of class type. Currently, the  
expression a == b is blindly rewritten as a.opEquals(b). I argue it  
should be rewritten into a call to an (imaginary/inlined) function  
equalObjects(a, b), with the following definition:


bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
 static if (is(U == class))
 {
 if (b is null) return a is null;
 if (a is null) return b is null;
 }
 else
 {
 enforce(a !is null);
 }
 return a.opEquals(b);
}

This hoists the identity test outside the opEquals call and also deals  
with null references. What do you think?



Andrei
 I like this. I think optimizing away opEquals for identical objects  
would also be a good idea:

 static if (is(U == class))
if(a is b || a is null || b  is null) return a is b;
else
enforce(a !is null);


This code has an inefficiency, it seems, because it makes a bit more  
checks than necessary (e.g. checks a is b twice). Let's simplify:


bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
  static if (is(U == class))
  {
  if (a is b) return true;
  if (b is null || a is null) return false;
  }
  else
  {
  enforce(a !is null);
  }
  return a.opEquals(b);
}


Andrei


Are the extra branch and return statement faster? Besides, I thought the  
optimizer would cache a is b:


auto a_is_b = a is b;
if (a_is_b || b is null || a is null) return a_is_b;


Re: putting more smarts into a == b

2009-09-27 Thread Andrei Alexandrescu

Robert Jacques wrote:
On Sun, 27 Sep 2009 09:11:38 -0400, Andrei Alexandrescu 
seewebsiteforem...@erdani.org wrote:



Robert Jacques wrote:
On Sat, 26 Sep 2009 21:32:13 -0400, Andrei Alexandrescu 
seewebsiteforem...@erdani.org wrote:


Consider two objects a and b with a of class type. Currently, the 
expression a == b is blindly rewritten as a.opEquals(b). I argue it 
should be rewritten into a call to an (imaginary/inlined) function 
equalObjects(a, b), with the following definition:


bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
 static if (is(U == class))
 {
 if (b is null) return a is null;
 if (a is null) return b is null;
 }
 else
 {
 enforce(a !is null);
 }
 return a.opEquals(b);
}

This hoists the identity test outside the opEquals call and also 
deals with null references. What do you think?



Andrei
 I like this. I think optimizing away opEquals for identical objects 
would also be a good idea:

 static if (is(U == class))
if(a is b || a is null || b  is null) return a is b;
else
enforce(a !is null);


This code has an inefficiency, it seems, because it makes a bit more 
checks than necessary (e.g. checks a is b twice). Let's simplify:


bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
  static if (is(U == class))
  {
  if (a is b) return true;
  if (b is null || a is null) return false;
  }
  else
  {
  enforce(a !is null);
  }
  return a.opEquals(b);
}


Andrei


Are the extra branch and return statement faster?


Just as fast. Short-circuit evaluation also generates code with branches.

Besides, I thought the 
optimizer would cache a is b:


auto a_is_b = a is b;
if (a_is_b || b is null || a is null) return a_is_b;


I'm trying to not rely on such...


Andrei


Re: putting more smarts into a == b

2009-09-27 Thread Ary Borenszweig

Andrei Alexandrescu wrote:

Frank Benoit wrote:

Andrei Alexandrescu schrieb:

Frank Benoit wrote:

Andrei Alexandrescu schrieb:

Consider two objects a and b with a of class type. Currently, the
expression a == b is blindly rewritten as a.opEquals(b). I argue it
should be rewritten into a call to an (imaginary/inlined) function
equalObjects(a, b), with the following definition:

bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
static if (is(U == class))
{
if (b is null) return a is null;
if (a is null) return b is null;
}
else
{
enforce(a !is null);
}
return a.opEquals(b);
}

This hoists the identity test outside the opEquals call and also deals
with null references. What do you think?


Andrei

What about interfaces?

Good question! What do they do now? I ran this:

interface A {}
class Widget : A {}

void main() {
auto a = cast(A) new Widget;
A b = null;
writeln(a == b);
writeln(b == a);
}

To my surprise, the program printed false twice. If I replace A with
Widget inside main, the program prints false then crashes with the
mythical segfault :o).

So how are interfaces compared?


Andrei


Hm, i would have expected it not to compile, because A does not have
opEquals.

In DWT, I cast always first to Object.
Java if( intf1.equals(intf2) ){
D1.0 if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){


I think in D the cast is inserted automatically. Walter?

Andrei


Using the compile-time view of Descent, if I have this code:

---
interface I {

}

class C {

}

int main(char[][] args) {
C c = new C();
I i = null;

auto x = i == c;
auto y = c == i;

return 0;
}
---

the compiler turns it into:

---
interface I {
}

class C: Object {
}

int main(char[][] args) {
C c = new C;
I i = null;
int x = c.opEquals(cast(Object) i);
int y = c.opEquals(cast(Object) i);
return 0;
}
---

That's why it doesn't segfault.

Debugging the code it turns out the logic is very simple, it's just 
applying operator overloading: (for the first comparison) opEquals is 
searched in I. Since opEquals is not found in it, the compiler 
checks if opEquals is commutative. It is, so it searches opEquals in 
C, finds it and does the rewrite. (more or less)


putting more smarts into a == b

2009-09-26 Thread Andrei Alexandrescu
Consider two objects a and b with a of class type. Currently, the 
expression a == b is blindly rewritten as a.opEquals(b). I argue it 
should be rewritten into a call to an (imaginary/inlined) function 
equalObjects(a, b), with the following definition:


bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
static if (is(U == class))
{
if (b is null) return a is null;
if (a is null) return b is null;
}
else
{
enforce(a !is null);
}
return a.opEquals(b);
}

This hoists the identity test outside the opEquals call and also deals 
with null references. What do you think?



Andrei


Re: putting more smarts into a == b

2009-09-26 Thread Jarrett Billingsley
On Sat, Sep 26, 2009 at 9:32 PM, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:
 Consider two objects a and b with a of class type. Currently, the expression
 a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten
 into a call to an (imaginary/inlined) function equalObjects(a, b), with the
 following definition:

 bool equalObjects(T, U)(T a, U b) if (is(T == class))
 {
    static if (is(U == class))
    {
        if (b is null) return a is null;
        if (a is null) return b is null;
    }
    else
    {
        enforce(a !is null);
    }
    return a.opEquals(b);
 }

 This hoists the identity test outside the opEquals call and also deals with
 null references. What do you think?

I'm almost sure that C# does this already, and it's a useful behavior.

Of course, with nonnull types, the check for null wouldn't even need
to exst ;)


Re: putting more smarts into a == b

2009-09-26 Thread bearophile
Andrei Alexandrescu:

 This hoists the identity test outside the opEquals call and also deals 
 with null references. What do you think?

I like it, is also saves some boring code.

Bye,
bearophile


Re: putting more smarts into a == b

2009-09-26 Thread Ary Borenszweig

Andrei Alexandrescu wrote:
Consider two objects a and b with a of class type. Currently, the 
expression a == b is blindly rewritten as a.opEquals(b). I argue it 
should be rewritten into a call to an (imaginary/inlined) function 
equalObjects(a, b)


Definitely!


Re: putting more smarts into a == b

2009-09-26 Thread Robert Jacques
On Sat, 26 Sep 2009 21:32:13 -0400, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:


Consider two objects a and b with a of class type. Currently, the  
expression a == b is blindly rewritten as a.opEquals(b). I argue it  
should be rewritten into a call to an (imaginary/inlined) function  
equalObjects(a, b), with the following definition:


bool equalObjects(T, U)(T a, U b) if (is(T == class))
{
 static if (is(U == class))
 {
 if (b is null) return a is null;
 if (a is null) return b is null;
 }
 else
 {
 enforce(a !is null);
 }
 return a.opEquals(b);
}

This hoists the identity test outside the opEquals call and also deals  
with null references. What do you think?



Andrei


I like this. I think optimizing away opEquals for identical objects would  
also be a good idea:


static if (is(U == class))
if(a is b || a is null || b  is null) return a is b;
else
enforce(a !is null);