Re: Weird opEquals Problem

2012-02-23 Thread H. S. Teoh
On Thu, Feb 23, 2012 at 10:20:58PM +1300, James Miller wrote:
> On 23 February 2012 15:55, H. S. Teoh  wrote:
> > On Wed, Feb 22, 2012 at 09:32:55PM -0500, Kevin wrote:
> > [...]
> >> Thanks for all the help.  Although you guys should be careful
> >> because if you keep giving such in depth answers I might start
> >> asking stupid questions just to learn the language in more depth :)
[...]
> Just don't start asking too stupid questions (like how does 1+1 work?)
> :P.
[...]

Well, that question may not be that stupid if you're talking about it
from the context of operator overloading... Or code generation. Or
compile-time constant folding. ;-)


T

-- 
MAS = Mana Ada Sistem?


Re: Weird opEquals Problem

2012-02-23 Thread James Miller
On 23 February 2012 15:55, H. S. Teoh  wrote:
> On Wed, Feb 22, 2012 at 09:32:55PM -0500, Kevin wrote:
> [...]
>> Thanks for all the help.  Although you guys should be careful because
>> if you keep giving such in depth answers I might start asking stupid
>> questions just to learn the language in more depth :)
>
> We don't mind. :-)
>
>
> T
>
> --
> Truth, Sir, is a cow which will give [skeptics] no more milk, and so
> they are gone to milk the bull. -- Sam. Johnson

Just don't start asking too stupid questions (like how does 1+1 work?) :P.

--
James Miller


Re: Weird opEquals Problem

2012-02-22 Thread H. S. Teoh
On Wed, Feb 22, 2012 at 09:32:55PM -0500, Kevin wrote:
[...]
> Thanks for all the help.  Although you guys should be careful because
> if you keep giving such in depth answers I might start asking stupid
> questions just to learn the language in more depth :)

We don't mind. :-)


T

-- 
Truth, Sir, is a cow which will give [skeptics] no more milk, and so
they are gone to milk the bull. -- Sam. Johnson


Re: Weird opEquals Problem

2012-02-22 Thread H. S. Teoh
On Wed, Feb 22, 2012 at 09:32:02PM -0500, Jonathan M Davis wrote:
> On Wednesday, February 22, 2012 18:22:59 H. S. Teoh wrote:
> > On Wed, Feb 22, 2012 at 09:08:24PM -0500, Jonathan M Davis wrote:
[...]
> > > if(a is null || b is null)
> > > return false;
> > 
> > IIRC that line should read:
> > 
> > if (a is null && b !is null || a !is null && b is null)
> 
> Why? If you have
> 
> if(a is b)
> 
> (which should be there for efficiency if nothing else), then you've
> already verified that they're not both null, so there's no point in
> checking that they aren't both null like you're doing there.

You could be right. I'm just writing from my memory of what TDPL says. I
could be wrong.


> If the actual implementation is doing what you suggest, it should be
> fixed.

Yes.


> > Somewhere in here is also:
> > 
> > if (typeof(a)==typeof(b))
> > return a.opEquals(b);
> > 
> > as a slightly optimization for the case when both are the same type.
> 
> Possibly, I don't recall for sure. It wouldn't surprise me.

Gah, we're both working from our possibly unreliable memories. So I
looked up the actual source code on github:

equals_t opEquals(Object lhs, Object rhs)
{
if (lhs is rhs)
return true;
if (lhs is null || rhs is null)
return false;
if (typeid(lhs) == typeid(rhs))
return lhs.opEquals(rhs);
return lhs.opEquals(rhs) &&
   rhs.opEquals(lhs);
}


[...]
> > This is one of the little things that makes D shine. It's part of
> > D's motto of "the simplest thing to write should default to the
> > safest, most correct behaviour".
> > 
> > It jives with a principle I've always believed in when it comes to
> > programming languages (or any computer language): "Simple things
> > should be simple, and hard things should be possible." In this, D
> > wins over Java by both counts, because Java's verbosity violates
> > "simple things should be simple", and Java completely ruling out
> > low-level operations (e.g. to write a GC in Java) fails "hard things
> > should be possible".
> 
> As much as that may be true, his particular case is just a cut and
> dried case of D learning from Java's mistakes as opposed to the
> language being different due to different design goals.

Well, I was speaking from the viewpoint of a language user rather than a
language designer. :) I had been using C/C++ for about two decades, the
latter half of which saw me searching for (or perhaps inventing) a
better language with equivalent power/performance. Those years of
experience with C/C++ led me to hold rather high standards for my
"ideal" language, so it was rather pleasant to discover how much D
matched my requirements.


> C# managed to improved on Java on a number of counts even though it's
> a very similar language, simply because it was able to learn from
> Java's mistakes.  Really, that's one of C++'s biggest problems. It was
> the first to do a lot of stuff, and the languages which followed
> learned from _its_ mistakes. D, being newer than all three of those
> languages, should have been able to improve upon them even if it
> didn't have all of the new and innovative features that it has.

Very true. Hindsight is always 20/20, as they say. Although C++ has
tried to fix itself over the years (esp. with C++11), it simply couldn't
overcome the fundamental flaws, as that would break too much existing
code.


> I do agree though that D strikes a much better balance than Java does.
[...]

I would argue D as a language is far superior to Java in many, many
ways. Even if the current implementation could do with some improvement.
;-)


T

-- 
MSDOS = MicroSoft's Denial Of Service


Re: Weird opEquals Problem

2012-02-22 Thread Kevin

On 02/22/2012 09:32 PM, H. S. Teoh wrote:
The reason is that D guarantees that "==" is: (1) reflexive (a==a for 
any a); (2) symmetric (a==b if and only if b==a); and (3) transitive 
(if a==b and b==c then a==c). This allows the compiler to safely 
optimize expressions containing == comparisons without changing the 
code's semantics. Allowing one-sided comparisons like you suggest 
would break symmetry (a.opEquals(b) != b.opEquals(a)), and likely 
transitivity as well. T 

Fair enough.  I love how everything in D has reasons.

Thanks for all the help.  Although you guys should be careful because if 
you keep giving such in depth answers I might start asking stupid 
questions just to learn the language in more depth :)


Re: Weird opEquals Problem

2012-02-22 Thread Jonathan M Davis
On Wednesday, February 22, 2012 18:22:59 H. S. Teoh wrote:
> On Wed, Feb 22, 2012 at 09:08:24PM -0500, Jonathan M Davis wrote:
> [...]
> 
> > I don't remember _exactly_ how the free function version of opEquals lined
> > out (I believe that TDPL explains it),
> 
> Yes, TDPL lays out the entire function (slightly simplified).
> 
> > but it's something like
> > 
> > bool opEquals(Object a, Object b)
> > {
> > 
> > if(a is b)
> > return true;
> > 
> > if(a is null || b is null)
> > return false;
> 
> IIRC that line should read:
> 
> if (a is null && b !is null || a !is null && b is null)

Why? If you have

if(a is b)

(which should be there for efficiency if nothing else), then you've already 
verified that they're not both null, so there's no point in checking that they 
aren't both null like you're doing there. If the actual implementation is
doing what you suggest, it should be fixed.

> Somewhere in here is also:
> 
> if (typeof(a)==typeof(b))
> return a.opEquals(b);
> 
> as a slightly optimization for the case when both are the same type.

Possibly, I don't recall for sure. It wouldn't surprise me.

> > This helps some of the problems you get with opEquals in other
> > languages such as Java. It also checks for null, so you don't have to
> > worry about a null object causing a segfault.
> 
> This is one of the little things that makes D shine. It's part of D's
> motto of "the simplest thing to write should default to the safest, most
> correct behaviour".
> 
> It jives with a principle I've always believed in when it comes to
> programming languages (or any computer language): "Simple things should
> be simple, and hard things should be possible." In this, D wins over
> Java by both counts, because Java's verbosity violates "simple things
> should be simple", and Java completely ruling out low-level operations
> (e.g. to write a GC in Java) fails "hard things should be possible".

As much as that may be true, his particular case is just a cut and dried case 
of D learning from Java's mistakes as opposed to the language being different 
due to different design goals. C# managed to improved on Java on a number of 
counts even though it's a very similar language, simply because it was able to 
learn from Java's mistakes. Really, that's one of C++'s biggest problems. It 
was the first to do a lot of stuff, and the languages which followed learned 
from _its_ mistakes. D, being newer than all three of those languages, should 
have been able to improve upon them even if it didn't have all of the new and 
innovative features that it has.

I do agree though that D strikes a much better balance than Java does.

- Jonathan M Davis


Re: Weird opEquals Problem

2012-02-22 Thread H. S. Teoh
On Wed, Feb 22, 2012 at 09:17:17PM -0500, Kevin Cox wrote:
> Oh, ok.
> 
> First of all the docs appear somewhat misleading.  I thought that function
> was an example on how to overload it.  That could be clarified a little.
> 
> Second of all, isn't that inefficient?  And  if you wanted to be able to
> compare to another type you don't control?  I think it would make more
> sense to have it pick the best match much like other overloads.

Well, that's only part of the story. :) The full story is that "a==b"
actually translates to something like this:

if (a is b)
return true;

if (a is null && b !is null || a !is null && b is null)
return false;

if (typeof(a) == typeof(b))
return a.opEquals(b);

return a.opEquals(b) && b.opEquals(a);

So in the simple cases (one argument is null, or both objects are the
same type) only one call to opEquals is made. Only in the full general
case you get two calls.

As for comparing against a type you don't control, the best bet is to
write your own comparison function (named something other than
"opEquals"). For example:

class A {
bool compare(Object o) {
// I like to be equal to everything
return true;
}
}
class B {
}

auto a = new A;
auto b = new B;
assert(a.compare(b));

The reason is that D guarantees that "==" is:

(1) reflexive (a==a for any a);
(2) symmetric (a==b if and only if b==a); and
(3) transitive (if a==b and b==c then a==c).

This allows the compiler to safely optimize expressions containing ==
comparisons without changing the code's semantics.

Allowing one-sided comparisons like you suggest would break symmetry
(a.opEquals(b) != b.opEquals(a)), and likely transitivity as well.


T

-- 
Chance favours the prepared mind. -- Louis Pasteur


Re: Weird opEquals Problem

2012-02-22 Thread Kevin

On 02/22/2012 09:22 PM, H. S. Teoh wrote:

"Simple things should be simple, and hard things should be possible."
Completely agreed.  Also reminds me of FreeBSD's "motto" "We don't 
prevent you from doing something really stupid because that could 
prevent you from doing something really clever."




Re: Weird opEquals Problem

2012-02-22 Thread H. S. Teoh
On Wed, Feb 22, 2012 at 09:08:24PM -0500, Jonathan M Davis wrote:
[...]
> I don't remember _exactly_ how the free function version of opEquals lined 
> out 
> (I believe that TDPL explains it),

Yes, TDPL lays out the entire function (slightly simplified).


> but it's something like
> 
> bool opEquals(Object a, Object b)
> {
>  if(a is b)
>  return true;
> 
>  if(a is null || b is null)
>  return false;

IIRC that line should read:

if (a is null && b !is null || a !is null && b is null)

Somewhere in here is also:

if (typeof(a)==typeof(b))
return a.opEquals(b);

as a slightly optimization for the case when both are the same type.

>  return a.opEquals(b) && b.opEquals(a);
> }
> 
> This helps some of the problems you get with opEquals in other
> languages such as Java. It also checks for null, so you don't have to
> worry about a null object causing a segfault.

This is one of the little things that makes D shine. It's part of D's
motto of "the simplest thing to write should default to the safest, most
correct behaviour".

It jives with a principle I've always believed in when it comes to
programming languages (or any computer language): "Simple things should
be simple, and hard things should be possible." In this, D wins over
Java by both counts, because Java's verbosity violates "simple things
should be simple", and Java completely ruling out low-level operations
(e.g. to write a GC in Java) fails "hard things should be possible".


T

-- 
Life is unfair. Ask too much from it, and it may decide you don't
deserve what you have now either.


Re: Weird opEquals Problem

2012-02-22 Thread Kevin Cox
Oh, ok.

First of all the docs appear somewhat misleading.  I thought that function
was an example on how to overload it.  That could be clarified a little.

Second of all, isn't that inefficient?  And  if you wanted to be able to
compare to another type you don't control?  I think it would make more
sense to have it pick the best match much like other overloads.
On Feb 22, 2012 9:05 PM, "H. S. Teoh"  wrote:

> On Wed, Feb 22, 2012 at 08:51:50PM -0500, Kevin wrote:
> > I have the following code which gives the same result on ldc2 and
> > dmd.  If I compare two objects of different classes I always get
> > false even though the comparator is called.
> [...]
> > The key thing to notice is that opEquals() gets called both times.
> > Any ideas about what is happening?
>
> It's because when A and B are different types, the compiler translates
> A==B to:
>
>A.opEquals(B) && B.opEquals(A)
>
> Both parties need to agree before they are considered equal.
>
>
> T
>
> --
> Shin: (n.) A device for finding furniture in the dark.
>


Re: Weird opEquals Problem

2012-02-22 Thread Jonathan M Davis
On Wednesday, February 22, 2012 20:51:50 Kevin wrote:
> I have the following code which gives the same result on ldc2 and dmd.
> If I compare two objects of different classes I always get false even
> though the comparator is called.

> The key thing to notice is that opEquals() gets called both times. Any
> ideas about what is happening?

That's easy. It's because opEquals must be true in both directions, and it's 
not. a == b does _not_ do a.opEquals(b) in D. It does opEquals(a, b), where 
opEquals is a free function. And Object's opEquals does not consider the two 
to be equal.

I don't remember _exactly_ how the free function version of opEquals lined out 
(I believe that TDPL explains it), but it's something like

bool opEquals(Object a, Object b)
{
 if(a is b)
 return true;

 if(a is null || b is null)
 return false;

 return a.opEquals(b) && b.opEquals(a);
}

This helps some of the problems you get with opEquals in other languages such 
as Java. It also checks for null, so you don't have to worry about a null 
object causing a segfault.

If you added an opEquals to B which printed, you'd see it print as well. If 
you make it return false, you'll get the same behavior as now, and if you make 
it return true, then your program will work.

- Jonathan M Davis


Re: Weird opEquals Problem

2012-02-22 Thread H. S. Teoh
On Wed, Feb 22, 2012 at 08:51:50PM -0500, Kevin wrote:
> I have the following code which gives the same result on ldc2 and
> dmd.  If I compare two objects of different classes I always get
> false even though the comparator is called.
[...]
> The key thing to notice is that opEquals() gets called both times.
> Any ideas about what is happening?

It's because when A and B are different types, the compiler translates
A==B to:

A.opEquals(B) && B.opEquals(A)

Both parties need to agree before they are considered equal.


T

-- 
Shin: (n.) A device for finding furniture in the dark.


Weird opEquals Problem

2012-02-22 Thread Kevin
I have the following code which gives the same result on ldc2 and dmd.  
If I compare two objects of different classes I always get false even 
though the comparator is called.


The code:
~~
import std.stdio;

class A
{
override bool opEquals(Object o)
{
writeln("In opEquals()");
return true;
}
}

class B
{

}

void main()
{
auto o1 = new A();
auto o2 = new A();
auto o3 = new B();

writeln(1);
assert( o1 == o2 );
writeln(2);
assert( o1 == o3 ); // line 26.
}
~

The output:
~
$ dmd -w test.d && ./test
1
In opEquals()
2
In opEquals()
core.exception.AssertError@test(26): Assertion failure

./test() [0x804b246]
./test() [0x8049f32]
./test() [0x8049902]
./test() [0x804b848]
./test() [0x804b355]
./test() [0x804b88f]
./test() [0x804b355]
./test() [0x804b304]
/lib/libc.so.6(__libc_start_main+0xf3) [0xb7527483]

~

The key thing to notice is that opEquals() gets called both times.  Any 
ideas about what is happening?