View mode: basic / threaded / horizontal-split · Log in · Help
March 26, 2003
null == o?
In the documentation it says that the expression (a == b) is rewritten as
a.equals(b).

Given that, what happens if a is null? NullPointerException, or is there a
"safe-this" mechanism, ie. a.this is automatically checked against null? If
not automatic, is it possible to write something such as

class X
{
 int value;

 int eq(X rhs)
 {
   if(this === rhs)
   {
     return 1;
   }
   else if(rhs === null)
   {
      return false;
   }
   else if(rhs === null)
   {
      return false;
   }
   else
   {
     return this.value == rhs.value;
   }
 }
}

(I expect I've got some syntax, wrong, but presumably you know what I mean.)

This implementation then supports

X    x1     =     new X(1);
X    x2    =    new X(3);
X    x3    =    null;

if(x1 == x2)  // (i)
{}
if(x1 == x3)  // (ii)
{}
if(x3 == x2)  // (iii)
{}

If all this is not supported, what would be the result of (iii) - crash?
March 26, 2003
Re: null == o?
"Matthew Wilson" <dmd@synesis.com.au> escreveu na mensagem
news:b5r9l2$2r2u$1@digitaldaemon.com...
> In the documentation it says that the expression (a == b) is rewritten as
> a.equals(b).
>
> Given that, what happens if a is null? NullPointerException, or is there a
> "safe-this" mechanism, ie. a.this is automatically checked against null?
If
> not automatic, is it possible to write something such as
>
> class X
> {
>   int value;
>
>   int eq(X rhs)
>   {
>     if(this === rhs)
>     {
>       return 1;
>     }
>     else if(rhs === null)
            ^^^^^^ -> lhs

>     {
>        return false;
>     }
>     else if(rhs === null)
>     {
>        return false;
>     }
>     else
>     {
>       return this.value == rhs.value;
>     }
>   }
> }
>
> (I expect I've got some syntax, wrong, but presumably you know what I
mean.)
>
> This implementation then supports
>
> X    x1     =     new X(1);
> X    x2    =    new X(3);
> X    x3    =    null;
>
> if(x1 == x2)  // (i)
> {}
> if(x1 == x3)  // (ii)
> {}
> if(x3 == x2)  // (iii)
> {}
>
> If all this is not supported, what would be the result of (iii) - crash?
>

Access violation. It's safer to use a generic equals function that will do
the appropriate checks:


equals(Object left, Object right) {
   if (left === right) {
       return true;
   } else if ((left === null) || (right === null)) {
       return false;
   } else {
       return left == right;
   }
}


If (iii) was valid we could never be certain to use "this" reference inside
methods, because it could be null. But the invariant of Object is
"assert(this !== null);".


---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.463 / Virus Database: 262 - Release Date: 17/3/2003
March 27, 2003
Re: null == o?
Understood.

However, I have to strongly disagree with you - or is it Walter? - that the
D comparison idiom is to eschew direct use of == and === and go for
something like your equals(Object left, Object right).

This seems to be the worst of all possible worlds. D's bitten by its own
cleverness here. Consider:

Java sucks because == works differently for fundamental and object types,
necessitating the inconsistency of using equals() for object types.

C# attempts to address this with operator overloading, but it still leaves
one clueless as to whether any reference comparisons are comparing
equivalence or identity. This also sucks, unless one "trusts" the author of
a class. (Got to love that trust ... it never let's you down, eh?)

C++ is entirely straightforward in that == on pointers always checks
identity and on references/instances always checks equivalence.
Notwithstanding this entirely reliable consistency, it is a source of
confusion to novices simply because pointers and references are difficult
concepts to absorb and disambiguate.

D sensibly addresses this confusing issue once and for all by separating
equivalence and identity into separate operators, and making them
syntatically consistent, hence my comment of satisfaction in the "Identity &
equivalence" thread. However, this opportunity for clarity seems to have
been thrown if what many of you are telling me is correct, which is that

class X
{
}

X    y = . . .;

void f(X x)
{
 if(x == y)

will cause an access violation if x or y are null. It makes a nonsense of
the attempt to deal with the issue. If this cannot be fixed with == (and
===) syntax, then we're probably better off removing these operators from
the language and forcing use of Object.equals() and Object.equalsRef(). For
my part, though, that'd be gross. So:

It seems that we must find a way to sensibly incorporate === in ==. We could
engineer the compiler, or mandate the class author, to incorporate identity
checks in equivalence comparisons. However, there are circumstances in which
the call to === would be superfluous, reducing efficiency.

afaics, we need to cater for the following scenarios

if(x == y)

(i) x and y are non-null
(ii) x is null
(iii) y is null
(iv) x and y are null

the identity comparison _must_ be involved in == for (ii) and (iv), and
probably for (iii). I suggest that classes have two comparison functions,
both static, called eq() and eqi().

eq() compares two references for equality. It may assume that both
references are guaranteed non-null. If eq() is not supported by a class, the
compiler provides "a bit compare of the contents of the two structs is done
to determine equality or inequality" as per the current fashion.

eqi() checks for references for equality, mindful of identity. It checks
whether either or both of the references are null, and if they are not it
returns the result of calling eq() on them. The default eqi() implementation
would be

class X
{
 static int eqi(X lhs, X rhs)
 {
   if(lhs === rhs) // Identity comparison
   {
     return true; // Same instance, therefore equal
   }
   else if(lhs === null &&
            rhs === null)
   {
     0; // One, but not both (because of check above), is null, so not
equal
   }
   else
   {
     return X.eq(lhs, rhs); // Both non-null, do equivalence comparison
 }

The author of a class can provide

- neither
- eq() only - the usual case
- eqi() only
- eq() and eqi()

(I can't imagine a reason for either of the last two, but one never knows
...)

It would be *entirely* up to the compiler as to whether it translates x == y
as a call to X.eqi(x, y) or to X.eqi(x, y), presumably based on what it
knows about the context of the two references. This means that any
user-defined implementations of eq() and eqi() should be written with this
in mind, presumably meaning that eqi() would do anything other than the
default as shown above, apart from logging or other benign things.

Given static eq() and static eqi() we would no longer have non-static eq()
as a special function, and could likely get rid of it. (I haven't thought
about that part yet.)

Sound ok?

Comments appreciated. I certainly can't see how we can have access
violations when using == in expressions!!

Matthew


"Daniel Yokomiso" <daniel_yokomiso@yahoo.com.br> wrote in message
news:b5s1hg$ca1$1@digitaldaemon.com...
> "Matthew Wilson" <dmd@synesis.com.au> escreveu na mensagem
> news:b5r9l2$2r2u$1@digitaldaemon.com...
> > In the documentation it says that the expression (a == b) is rewritten
as
> > a.equals(b).
> >
> > Given that, what happens if a is null? NullPointerException, or is there
a
> > "safe-this" mechanism, ie. a.this is automatically checked against null?
> If
> > not automatic, is it possible to write something such as
> >
> > class X
> > {
> >   int value;
> >
> >   int eq(X rhs)
> >   {
> >     if(this === rhs)
> >     {
> >       return 1;
> >     }
> >     else if(rhs === null)
>              ^^^^^^ -> lhs
>
> >     {
> >        return false;
> >     }
> >     else if(rhs === null)
> >     {
> >        return false;
> >     }
> >     else
> >     {
> >       return this.value == rhs.value;
> >     }
> >   }
> > }
> >
> > (I expect I've got some syntax, wrong, but presumably you know what I
> mean.)
> >
> > This implementation then supports
> >
> > X    x1     =     new X(1);
> > X    x2    =    new X(3);
> > X    x3    =    null;
> >
> > if(x1 == x2)  // (i)
> > {}
> > if(x1 == x3)  // (ii)
> > {}
> > if(x3 == x2)  // (iii)
> > {}
> >
> > If all this is not supported, what would be the result of (iii) - crash?
> >
>
> Access violation. It's safer to use a generic equals function that will do
> the appropriate checks:
>
>
> equals(Object left, Object right) {
>     if (left === right) {
>         return true;
>     } else if ((left === null) || (right === null)) {
>         return false;
>     } else {
>         return left == right;
>     }
> }
>
>
> If (iii) was valid we could never be certain to use "this" reference
inside
> methods, because it could be null. But the invariant of Object is
> "assert(this !== null);".
>
>
> ---
> Outgoing mail is certified Virus Free.
> Checked by AVG anti-virus system (http://www.grisoft.com).
> Version: 6.0.463 / Virus Database: 262 - Release Date: 17/3/2003
>
>
March 27, 2003
Re: null == o?
Matthew Wilson wrote:
> Understood.
> 
> However, I have to strongly disagree with you - or is it Walter? - that the
> D comparison idiom is to eschew direct use of == and === and go for
> something like your equals(Object left, Object right).
> 
> This seems to be the worst of all possible worlds. D's bitten by its own
> cleverness here. Consider:
> 
> Java sucks because == works differently for fundamental and object types,
> necessitating the inconsistency of using equals() for object types.
> 
> C# attempts to address this with operator overloading, but it still leaves
> one clueless as to whether any reference comparisons are comparing
> equivalence or identity. This also sucks, unless one "trusts" the author of
> a class. (Got to love that trust ... it never let's you down, eh?)
> 
> C++ is entirely straightforward in that == on pointers always checks
> identity and on references/instances always checks equivalence.
> Notwithstanding this entirely reliable consistency, it is a source of
> confusion to novices simply because pointers and references are difficult
> concepts to absorb and disambiguate.
> 
> D sensibly addresses this confusing issue once and for all by separating
> equivalence and identity into separate operators, and making them
> syntatically consistent, hence my comment of satisfaction in the "Identity &
> equivalence" thread. However, this opportunity for clarity seems to have
> been thrown if what many of you are telling me is correct, which is that
> 
> class X
> {
> }
> 
> X    y = . . .;
> 
> void f(X x)
> {
>   if(x == y)
> 
> will cause an access violation if x or y are null. It makes a nonsense of
> the attempt to deal with the issue. If this cannot be fixed with == (and
> ===) syntax, then we're probably better off removing these operators from
> the language and forcing use of Object.equals() and Object.equalsRef(). For
> my part, though, that'd be gross. So:
> 
> It seems that we must find a way to sensibly incorporate === in ==. We could
> engineer the compiler, or mandate the class author, to incorporate identity
> checks in equivalence comparisons. However, there are circumstances in which
> the call to === would be superfluous, reducing efficiency.
> 
> afaics, we need to cater for the following scenarios
> 
> if(x == y)
> 
> (i) x and y are non-null
> (ii) x is null
> (iii) y is null
> (iv) x and y are null
> 
> the identity comparison _must_ be involved in == for (ii) and (iv), and
> probably for (iii). I suggest that classes have two comparison functions,
> both static, called eq() and eqi().
> 
> eq() compares two references for equality. It may assume that both
> references are guaranteed non-null. If eq() is not supported by a class, the
> compiler provides "a bit compare of the contents of the two structs is done
> to determine equality or inequality" as per the current fashion.
> 
> eqi() checks for references for equality, mindful of identity. It checks
> whether either or both of the references are null, and if they are not it
> returns the result of calling eq() on them. The default eqi() implementation
> would be
> 
> class X
> {
>   static int eqi(X lhs, X rhs)
>   {
>     if(lhs === rhs) // Identity comparison
>     {
>       return true; // Same instance, therefore equal
>     }
>     else if(lhs === null &&
>              rhs === null)
>     {
>       0; // One, but not both (because of check above), is null, so not
> equal
>     }
>     else
>     {
>       return X.eq(lhs, rhs); // Both non-null, do equivalence comparison
>   }
> 
> The author of a class can provide
> 
>  - neither
>  - eq() only - the usual case
>  - eqi() only
>  - eq() and eqi()
> 
> (I can't imagine a reason for either of the last two, but one never knows
> ...)
> 
> It would be *entirely* up to the compiler as to whether it translates x == y
> as a call to X.eqi(x, y) or to X.eqi(x, y), presumably based on what it
> knows about the context of the two references. This means that any
> user-defined implementations of eq() and eqi() should be written with this
> in mind, presumably meaning that eqi() would do anything other than the
> default as shown above, apart from logging or other benign things.
> 
> Given static eq() and static eqi() we would no longer have non-static eq()
> as a special function, and could likely get rid of it. (I haven't thought
> about that part yet.)
> 
> Sound ok?
> 
> Comments appreciated. I certainly can't see how we can have access
> violations when using == in expressions!!
> 
> Matthew
> 
> 
> "Daniel Yokomiso" <daniel_yokomiso@yahoo.com.br> wrote in message
> news:b5s1hg$ca1$1@digitaldaemon.com...
> 
>>"Matthew Wilson" <dmd@synesis.com.au> escreveu na mensagem
>>news:b5r9l2$2r2u$1@digitaldaemon.com...
>>
>>>In the documentation it says that the expression (a == b) is rewritten
> 
> as
> 
>>>a.equals(b).
>>>
>>>Given that, what happens if a is null? NullPointerException, or is there
> 
> a
> 
>>>"safe-this" mechanism, ie. a.this is automatically checked against null?
>>
>>If
>>
>>>not automatic, is it possible to write something such as
>>>
>>>class X
>>>{
>>>  int value;
>>>
>>>  int eq(X rhs)
>>>  {
>>>    if(this === rhs)
>>>    {
>>>      return 1;
>>>    }
>>>    else if(rhs === null)
>>
>>             ^^^^^^ -> lhs
>>
>>
>>>    {
>>>       return false;
>>>    }
>>>    else if(rhs === null)
>>>    {
>>>       return false;
>>>    }
>>>    else
>>>    {
>>>      return this.value == rhs.value;
>>>    }
>>>  }
>>>}
>>>
>>>(I expect I've got some syntax, wrong, but presumably you know what I
>>
>>mean.)
>>
>>>This implementation then supports
>>>
>>>X    x1     =     new X(1);
>>>X    x2    =    new X(3);
>>>X    x3    =    null;
>>>
>>>if(x1 == x2)  // (i)
>>>{}
>>>if(x1 == x3)  // (ii)
>>>{}
>>>if(x3 == x2)  // (iii)
>>>{}
>>>
>>>If all this is not supported, what would be the result of (iii) - crash?
>>>
>>
>>Access violation. It's safer to use a generic equals function that will do
>>the appropriate checks:
>>
>>
>>equals(Object left, Object right) {
>>    if (left === right) {
>>        return true;
>>    } else if ((left === null) || (right === null)) {
>>        return false;
>>    } else {
>>        return left == right;
>>    }
>>}
>>
>>
>>If (iii) was valid we could never be certain to use "this" reference
> 
> inside
> 
>>methods, because it could be null. But the invariant of Object is
>>"assert(this !== null);".
>>
>>
>>---
>>Outgoing mail is certified Virus Free.
>>Checked by AVG anti-virus system (http://www.grisoft.com).
>>Version: 6.0.463 / Virus Database: 262 - Release Date: 17/3/2003

Maybe this has been suggested before, but this could all be avoided by 
not defining the == operator for the Object class.  Thus, testing two 
objects for equivalence would typically be a compile-time error.

Classes that do define == would have to be mindful of null references, 
though.
March 27, 2003
Re: null == o?
This could be more easily fixed by fixing == semantics.  When a and b 
are objects or pointers, then:

  a == b

Should be turned into:

  a === b || (a !== null && b !== null && a.eq (b))
March 27, 2003
Re: null == o?
I wholeheartedly agree. I need to be able to trust that I can use == and ===
without worrying about access violations for null pointers.

This is a big deal.

--Benji


In article <b5tsd9$1k9$1@digitaldaemon.com>, Matthew Wilson says...
>
>Understood.
>
>However, I have to strongly disagree with you - or is it Walter? - that the
>D comparison idiom is to eschew direct use of == and === and go for
>something like your equals(Object left, Object right).
>
>This seems to be the worst of all possible worlds. D's bitten by its own
>cleverness here. Consider:
>
>Java sucks because == works differently for fundamental and object types,
>necessitating the inconsistency of using equals() for object types.
>
>C# attempts to address this with operator overloading, but it still leaves
>one clueless as to whether any reference comparisons are comparing
>equivalence or identity. This also sucks, unless one "trusts" the author of
>a class. (Got to love that trust ... it never let's you down, eh?)
>
>C++ is entirely straightforward in that == on pointers always checks
>identity and on references/instances always checks equivalence.
>Notwithstanding this entirely reliable consistency, it is a source of
>confusion to novices simply because pointers and references are difficult
>concepts to absorb and disambiguate.
>
>D sensibly addresses this confusing issue once and for all by separating
>equivalence and identity into separate operators, and making them
>syntatically consistent, hence my comment of satisfaction in the "Identity &
>equivalence" thread. However, this opportunity for clarity seems to have
>been thrown if what many of you are telling me is correct, which is that
>
>class X
>{
>}
>
>X    y = . . .;
>
>void f(X x)
>{
>  if(x == y)
>
>will cause an access violation if x or y are null. It makes a nonsense of
>the attempt to deal with the issue. If this cannot be fixed with == (and
>===) syntax, then we're probably better off removing these operators from
>the language and forcing use of Object.equals() and Object.equalsRef(). For
>my part, though, that'd be gross. So:
>
>It seems that we must find a way to sensibly incorporate === in ==. We could
>engineer the compiler, or mandate the class author, to incorporate identity
>checks in equivalence comparisons. However, there are circumstances in which
>the call to === would be superfluous, reducing efficiency.
>
>afaics, we need to cater for the following scenarios
>
>if(x == y)
>
>(i) x and y are non-null
>(ii) x is null
>(iii) y is null
>(iv) x and y are null
>
>the identity comparison _must_ be involved in == for (ii) and (iv), and
>probably for (iii). I suggest that classes have two comparison functions,
>both static, called eq() and eqi().
>
>eq() compares two references for equality. It may assume that both
>references are guaranteed non-null. If eq() is not supported by a class, the
>compiler provides "a bit compare of the contents of the two structs is done
>to determine equality or inequality" as per the current fashion.
>
>eqi() checks for references for equality, mindful of identity. It checks
>whether either or both of the references are null, and if they are not it
>returns the result of calling eq() on them. The default eqi() implementation
>would be
>
>class X
>{
>  static int eqi(X lhs, X rhs)
>  {
>    if(lhs === rhs) // Identity comparison
>    {
>      return true; // Same instance, therefore equal
>    }
>    else if(lhs === null &&
>             rhs === null)
>    {
>      0; // One, but not both (because of check above), is null, so not
>equal
>    }
>    else
>    {
>      return X.eq(lhs, rhs); // Both non-null, do equivalence comparison
>  }
>
>The author of a class can provide
>
> - neither
> - eq() only - the usual case
> - eqi() only
> - eq() and eqi()
>
>(I can't imagine a reason for either of the last two, but one never knows
>...)
>
>It would be *entirely* up to the compiler as to whether it translates x == y
>as a call to X.eqi(x, y) or to X.eqi(x, y), presumably based on what it
>knows about the context of the two references. This means that any
>user-defined implementations of eq() and eqi() should be written with this
>in mind, presumably meaning that eqi() would do anything other than the
>default as shown above, apart from logging or other benign things.
>
>Given static eq() and static eqi() we would no longer have non-static eq()
>as a special function, and could likely get rid of it. (I haven't thought
>about that part yet.)
>
>Sound ok?
>
>Comments appreciated. I certainly can't see how we can have access
>violations when using == in expressions!!
>
>Matthew
>
>
>"Daniel Yokomiso" <daniel_yokomiso@yahoo.com.br> wrote in message
>news:b5s1hg$ca1$1@digitaldaemon.com...
>> "Matthew Wilson" <dmd@synesis.com.au> escreveu na mensagem
>> news:b5r9l2$2r2u$1@digitaldaemon.com...
>> > In the documentation it says that the expression (a == b) is rewritten
>as
>> > a.equals(b).
>> >
>> > Given that, what happens if a is null? NullPointerException, or is there
>a
>> > "safe-this" mechanism, ie. a.this is automatically checked against null?
>> If
>> > not automatic, is it possible to write something such as
>> >
>> > class X
>> > {
>> >   int value;
>> >
>> >   int eq(X rhs)
>> >   {
>> >     if(this === rhs)
>> >     {
>> >       return 1;
>> >     }
>> >     else if(rhs === null)
>>              ^^^^^^ -> lhs
>>
>> >     {
>> >        return false;
>> >     }
>> >     else if(rhs === null)
>> >     {
>> >        return false;
>> >     }
>> >     else
>> >     {
>> >       return this.value == rhs.value;
>> >     }
>> >   }
>> > }
>> >
>> > (I expect I've got some syntax, wrong, but presumably you know what I
>> mean.)
>> >
>> > This implementation then supports
>> >
>> > X    x1     =     new X(1);
>> > X    x2    =    new X(3);
>> > X    x3    =    null;
>> >
>> > if(x1 == x2)  // (i)
>> > {}
>> > if(x1 == x3)  // (ii)
>> > {}
>> > if(x3 == x2)  // (iii)
>> > {}
>> >
>> > If all this is not supported, what would be the result of (iii) - crash?
>> >
>>
>> Access violation. It's safer to use a generic equals function that will do
>> the appropriate checks:
>>
>>
>> equals(Object left, Object right) {
>>     if (left === right) {
>>         return true;
>>     } else if ((left === null) || (right === null)) {
>>         return false;
>>     } else {
>>         return left == right;
>>     }
>> }
>>
>>
>> If (iii) was valid we could never be certain to use "this" reference
>inside
>> methods, because it could be null. But the invariant of Object is
>> "assert(this !== null);".
>>
>>
>> ---
>> Outgoing mail is certified Virus Free.
>> Checked by AVG anti-virus system (http://www.grisoft.com).
>> Version: 6.0.463 / Virus Database: 262 - Release Date: 17/3/2003
>>
>>
>
>
March 27, 2003
Re: null == o?
Very much agreed. (Until I come across something else) this is the only
showstopper I can see in D.

Does anyone have an objection to my suggestion from an
efficiency/understanding/robustness point of view?

"Benji Smith" <Benji_member@pathlink.com> wrote in message
news:b5ven6$233d$1@digitaldaemon.com...
> I wholeheartedly agree. I need to be able to trust that I can use == and
===
> without worrying about access violations for null pointers.
>
> This is a big deal.
>
> --Benji
>
>
> In article <b5tsd9$1k9$1@digitaldaemon.com>, Matthew Wilson says...
> >
> >Understood.
> >
> >However, I have to strongly disagree with you - or is it Walter? - that
the
> >D comparison idiom is to eschew direct use of == and === and go for
> >something like your equals(Object left, Object right).
> >
> >This seems to be the worst of all possible worlds. D's bitten by its own
> >cleverness here. Consider:
> >
> >Java sucks because == works differently for fundamental and object types,
> >necessitating the inconsistency of using equals() for object types.
> >
> >C# attempts to address this with operator overloading, but it still
leaves
> >one clueless as to whether any reference comparisons are comparing
> >equivalence or identity. This also sucks, unless one "trusts" the author
of
> >a class. (Got to love that trust ... it never let's you down, eh?)
> >
> >C++ is entirely straightforward in that == on pointers always checks
> >identity and on references/instances always checks equivalence.
> >Notwithstanding this entirely reliable consistency, it is a source of
> >confusion to novices simply because pointers and references are difficult
> >concepts to absorb and disambiguate.
> >
> >D sensibly addresses this confusing issue once and for all by separating
> >equivalence and identity into separate operators, and making them
> >syntatically consistent, hence my comment of satisfaction in the
"Identity &
> >equivalence" thread. However, this opportunity for clarity seems to have
> >been thrown if what many of you are telling me is correct, which is that
> >
> >class X
> >{
> >}
> >
> >X    y = . . .;
> >
> >void f(X x)
> >{
> >  if(x == y)
> >
> >will cause an access violation if x or y are null. It makes a nonsense of
> >the attempt to deal with the issue. If this cannot be fixed with == (and
> >===) syntax, then we're probably better off removing these operators from
> >the language and forcing use of Object.equals() and Object.equalsRef().
For
> >my part, though, that'd be gross. So:
> >
> >It seems that we must find a way to sensibly incorporate === in ==. We
could
> >engineer the compiler, or mandate the class author, to incorporate
identity
> >checks in equivalence comparisons. However, there are circumstances in
which
> >the call to === would be superfluous, reducing efficiency.
> >
> >afaics, we need to cater for the following scenarios
> >
> >if(x == y)
> >
> >(i) x and y are non-null
> >(ii) x is null
> >(iii) y is null
> >(iv) x and y are null
> >
> >the identity comparison _must_ be involved in == for (ii) and (iv), and
> >probably for (iii). I suggest that classes have two comparison functions,
> >both static, called eq() and eqi().
> >
> >eq() compares two references for equality. It may assume that both
> >references are guaranteed non-null. If eq() is not supported by a class,
the
> >compiler provides "a bit compare of the contents of the two structs is
done
> >to determine equality or inequality" as per the current fashion.
> >
> >eqi() checks for references for equality, mindful of identity. It checks
> >whether either or both of the references are null, and if they are not it
> >returns the result of calling eq() on them. The default eqi()
implementation
> >would be
> >
> >class X
> >{
> >  static int eqi(X lhs, X rhs)
> >  {
> >    if(lhs === rhs) // Identity comparison
> >    {
> >      return true; // Same instance, therefore equal
> >    }
> >    else if(lhs === null &&
> >             rhs === null)
> >    {
> >      0; // One, but not both (because of check above), is null, so not
> >equal
> >    }
> >    else
> >    {
> >      return X.eq(lhs, rhs); // Both non-null, do equivalence comparison
> >  }
> >
> >The author of a class can provide
> >
> > - neither
> > - eq() only - the usual case
> > - eqi() only
> > - eq() and eqi()
> >
> >(I can't imagine a reason for either of the last two, but one never knows
> >...)
> >
> >It would be *entirely* up to the compiler as to whether it translates x
== y
> >as a call to X.eqi(x, y) or to X.eqi(x, y), presumably based on what it
> >knows about the context of the two references. This means that any
> >user-defined implementations of eq() and eqi() should be written with
this
> >in mind, presumably meaning that eqi() would do anything other than the
> >default as shown above, apart from logging or other benign things.
> >
> >Given static eq() and static eqi() we would no longer have non-static
eq()
> >as a special function, and could likely get rid of it. (I haven't thought
> >about that part yet.)
> >
> >Sound ok?
> >
> >Comments appreciated. I certainly can't see how we can have access
> >violations when using == in expressions!!
> >
> >Matthew
> >
> >
> >"Daniel Yokomiso" <daniel_yokomiso@yahoo.com.br> wrote in message
> >news:b5s1hg$ca1$1@digitaldaemon.com...
> >> "Matthew Wilson" <dmd@synesis.com.au> escreveu na mensagem
> >> news:b5r9l2$2r2u$1@digitaldaemon.com...
> >> > In the documentation it says that the expression (a == b) is
rewritten
> >as
> >> > a.equals(b).
> >> >
> >> > Given that, what happens if a is null? NullPointerException, or is
there
> >a
> >> > "safe-this" mechanism, ie. a.this is automatically checked against
null?
> >> If
> >> > not automatic, is it possible to write something such as
> >> >
> >> > class X
> >> > {
> >> >   int value;
> >> >
> >> >   int eq(X rhs)
> >> >   {
> >> >     if(this === rhs)
> >> >     {
> >> >       return 1;
> >> >     }
> >> >     else if(rhs === null)
> >>              ^^^^^^ -> lhs
> >>
> >> >     {
> >> >        return false;
> >> >     }
> >> >     else if(rhs === null)
> >> >     {
> >> >        return false;
> >> >     }
> >> >     else
> >> >     {
> >> >       return this.value == rhs.value;
> >> >     }
> >> >   }
> >> > }
> >> >
> >> > (I expect I've got some syntax, wrong, but presumably you know what I
> >> mean.)
> >> >
> >> > This implementation then supports
> >> >
> >> > X    x1     =     new X(1);
> >> > X    x2    =    new X(3);
> >> > X    x3    =    null;
> >> >
> >> > if(x1 == x2)  // (i)
> >> > {}
> >> > if(x1 == x3)  // (ii)
> >> > {}
> >> > if(x3 == x2)  // (iii)
> >> > {}
> >> >
> >> > If all this is not supported, what would be the result of (iii) -
crash?
> >> >
> >>
> >> Access violation. It's safer to use a generic equals function that will
do
> >> the appropriate checks:
> >>
> >>
> >> equals(Object left, Object right) {
> >>     if (left === right) {
> >>         return true;
> >>     } else if ((left === null) || (right === null)) {
> >>         return false;
> >>     } else {
> >>         return left == right;
> >>     }
> >> }
> >>
> >>
> >> If (iii) was valid we could never be certain to use "this" reference
> >inside
> >> methods, because it could be null. But the invariant of Object is
> >> "assert(this !== null);".
> >>
> >>
> >> ---
> >> Outgoing mail is certified Virus Free.
> >> Checked by AVG anti-virus system (http://www.grisoft.com).
> >> Version: 6.0.463 / Virus Database: 262 - Release Date: 17/3/2003
> >>
> >>
> >
> >
>
>
March 28, 2003
Re: null == o?
> This seems to be the worst of all possible worlds. D's bitten by its own
> cleverness here. Consider:

> Java sucks
> C# attempts
> C++ is entirely straightforward

Okay, I love C++ but something is entirely wrong if such an antiquated
language manages to provide the most reliable solution to something as basic
as this (at least if you're not a newbie).

I think anything we come up with is just going to be a kludge around a
fundamental problem that is created by not automatically initializing
objects when they are declared.

I wonder if it wouldn't be better to have the language implicitly create an
instance of the object when it is declared if it's not done explicitly:
blah blab;
blah blab=new blah;    //these to lines are equivalent
//if blab is global, the instance would be created before main is called

You could then say that it is illegal to assign null to an object reference.
I don't think it makes much sense to have a null object reference most of
the time anyhow.  And it is easy to add a "bit empty;" line to a class for
the few times when something like that might be useful.

This would avoid the problem completely.  This could in theory cause a
performance hit, but I think that in practice that would happen seldom
enough that it would be more than worth the robustness.  How often do you
declare something and don't instantiate it almost immediately?  Even when
you do, it's likely to be a one time only thing--that is to say, it's
probably not going to be something that happens over and over again inside a
loop or recursive function.
March 28, 2003
Re: null == o?
"Jon Allen" <jallen@minotstateu.edu> wrote in message
news:b603qc$2ljo$1@digitaldaemon.com...
> > This seems to be the worst of all possible worlds. D's bitten by its own
> > cleverness here. Consider:
>
> > Java sucks
> > C# attempts
> > C++ is entirely straightforward
>
> Okay, I love C++ but something is entirely wrong if such an antiquated
> language manages to provide the most reliable solution to something as
basic
> as this (at least if you're not a newbie).

Why do you say it's basic? C#, D & Java all attempt to fudge the dichotomous
nature of instances and their pointers for the sake of a (what some would
say specious) syntactic nicety. For the record, I'm not one of them in
principle, but no language other than C++ has yet managed this distinction
in a sensible and complete fashion. D is coming the closest, but if we have
to manually test for identity before we can test for equality then all that
syntactic nicety is just a waste of time.

Whatever anyone's opinion, I fail to see how this is a simple issue.

> I think anything we come up with is just going to be a kludge around a
> fundamental problem that is created by not automatically initializing
> objects when they are declared.

Whether you like it or not, a big reason that C, C++ and D have been
designed the way they have is for efficiency. C & C++ owe a lot of their
sucecss to this. You're not going to sell D as anything other than a lame
puppy if it cannot demonstrate similar, or better, levels of efficiency than
its forebears/peers. The work Walter and I have been doing in the last week
or so on performance comparisons has shown that D does indeed kick the arse
of most of its peers in a number of areas; being a C++ diehard myself, this
is a significant factor in my personnally starting to think more and more of
using D seriously.

I know there is a significant subset of the C/C++ community who prefer the
specious utility and "prettiness" of the iostreams and all those ridiculous
chevron operators, but every single serious C++ developer I have worked with
would run a mile before using them or any other inefficient thing.
(Obviously I'm talking about development where performance is important.
Other things one can be much more lax.) I am willing to bet large amounts of
reputation that all these same experienced guys (and gals) won't look to D
unless it can promise the same levels of performance.

> I wonder if it wouldn't be better to have the language implicitly create
an
> instance of the object when it is declared if it's not done explicitly:
> blah blab;
> blah blab=new blah;    //these to lines are equivalent
> //if blab is global, the instance would be created before main is called

[Q: I presume that D currently initialises an uninitialised variable to
null. Is this correct? If it doesn't, it should.]

I can't agree with this. Putting aside the fact that it could result in some
horrible inefficiencies for non-trivial class instances, it is overly
restrictive. Simplistically

class Something
{}

class QuiteSomething
 : Something
{}

class AmazingSomething
 : Something
{}


// Factory object
Something CreateSomethingSpecial(char[] parameterisation)
{
 Something    something = null; // The default case

 if(parameterisation == "quite-special")
 {
   something = new QuiteSomething();
 }
 else if(parameterisation == "amazing")
 {
   something = new AmazingSomething();
 }
 else if
 {
   . . . // etc. etc.

 return something;
}

What you're telling me is that I cannot write it like this. Either I have to
have multiple return statements - which is not my style - or I have cannot
return null so must use exceptions - which may not be appropriate/desirable.
What have we gained with this restriction?

> You could then say that it is illegal to assign null to an object
reference.
> I don't think it makes much sense to have a null object reference most of
> the time anyhow.

I have to strongly disagree with this, sorry. :)

null is a _very_ useful state. Without it we'd either have to have signal
objects, which is inefficient, or use exceptions for nearly everything.

>  And it is easy to add a "bit empty;" line to a class for
> the few times when something like that might be useful.
>
> This would avoid the problem completely.  This could in theory cause a
> performance hit, but I think that in practice that would happen seldom
> enough that it would be more than worth the robustness.  How often do you
> declare something and don't instantiate it almost immediately?  Even when
> you do, it's likely to be a one time only thing--that is to say, it's
> probably not going to be something that happens over and over again inside
a
> loop or recursive function.
>
>


All of the trouble so far is based on a desire to handle the mess of
equivalence/identity in languages that use references as pointers (i.e. the
references can be null). Unless there is an efficient built-in, but
potentially user-tailorable, handling of this then we might as well get rid
of the == and === operators entirely, and do everything with
Object.Equals(o1, o2) and Object.EqualsReference(o1, o2) methods, with all
the glamour that would entail.
March 28, 2003
Re: null == o?
Hi,

   Comments embedded.


"Matthew Wilson" <dmd@synesis.com.au> escreveu na mensagem
news:b5tsd9$1k9$1@digitaldaemon.com...
> Understood.
>
> However, I have to strongly disagree with you - or is it Walter? - that
the
> D comparison idiom is to eschew direct use of == and === and go for
> something like your equals(Object left, Object right).


   Don't blame Walter for my lame opinions ;-) I prefer to play safe if I
don't know what the values can be. If a variable x can be null, I want to
deal with this directly. Also, if it can't be null (contracts are wonderful
aren't they?) I fell safe to use "==". But my lazyness compels me to use
only a generic equals, because it will be symmetrical in my "eq" methods and
deal with nasty things for me (like null or NaN).


> This seems to be the worst of all possible worlds. D's bitten by its own
> cleverness here. Consider:
>
> Java sucks because == works differently for fundamental and object types,
> necessitating the inconsistency of using equals() for object types.


   Java (the language) is consistent, because "==" handle equality (i.e. if
two things are the same) and "equals" handle equivalence (i.e. if right now
two things have same values). I'll ignore the distinction between primitives
and object types. OTOH Java (the library) sometimes assumes that "equals"
means identity on "value types" (an imaginary concept in Java, because the
VM knows only primitives and references, modulo magic string stuff). You can
see this erroneous behaviour in the collections framework, most classes
assume that "equals" and "hashCode" are invariant. Equivalence can be used
to check if two lists of same size have the equivalent items and the same
indices: it is a transient query. Identity can be used to check if two
variables have the same value (same primitive, struct or same reference). A
simple example is always nice:

alias bit boolean;
boolean equals(char[] left, char[] right) {
   if (left === right) {
       return true;
   } else if ((null === left) || (null === right)) {
       return false;
   } else {
       return left[] == right[];
   }
}
class Customer {
   private final long id;
   private char[] name;
   private int age;
   this(long _id, char[] _name, int _age) {
       this.id = _id;
       this.name = _name;
       this.age = _age;
   }
   public boolean eq(Customer other) {
       return isEquivalentTo(other);
   }
   public boolean isSameAs(Customer other) {
       return this === other;
   }
   public boolean isEquivalentTo(Customer other) {
       if (other !==  null) {
           return (id == other.id) // primitive "==" is "==="
              && (age == other.age) // ditto
              && equals(name, other.name); // names can be null
       } else {
           return false;
       }
   }
   public boolean isEqualTo(Customer other) {
       if (other !==  null) {
           return (id == other.id); // our dreams ;-)
       } else {
           return false;
       }
   }
}

Customer charlie = new Customer(1, "Charlie", 8);
Customer mrBrown = new Customer(1, "Charlie", 27);

Customer peanutsDude = charlie;
Customer spuriousCharlie = new Customer(1, "Charlie", 8);

assert(charlie === peanutsDude);
assert(charlie.isSameAs(peanutsDude));
assert(!charlie.isSameAs(mrBrown));
assert(!charlie.isSameAs(spuriousCharlie));

assert(charlie == peanutsDude);
assert(charlie != mrBrown);
assert(charlie == spuriousCharlie);
assert(charlie.isEquivalentTo(peanutsDude));
assert(!charlie.isEquivalentTo(mrBrown));
assert(charlie.isEquivalentTo(spuriousCharlie));

assert(charlie.isEqualTo(peanutsDude));
assert(charlie.isEqualTo(mrBrown));
assert(charlie.isEqualTo(spuriousCharlie));


   In D identity is always variable value (primitive or struct or
reference). Equivalence is identity for primitives, "eq()" for objects and I
don't remember what for structs.

> C# attempts to address this with operator overloading, but it still leaves
> one clueless as to whether any reference comparisons are comparing
> equivalence or identity. This also sucks, unless one "trusts" the author
of
> a class. (Got to love that trust ... it never let's you down, eh?)


   C# allows one to write beatiful code (i.e. cast-fest) to compare for
identity IIRC, something like "(o1 as Object) == (o2 as Object)".


> C++ is entirely straightforward in that == on pointers always checks
> identity and on references/instances always checks equivalence.
> Notwithstanding this entirely reliable consistency, it is a source of
> confusion to novices simply because pointers and references are difficult
> concepts to absorb and disambiguate.


   I thought C++ depended on "operator==" definition's. Can't one define a
"operator==" for pointers that do the wrong thing?


> D sensibly addresses this confusing issue once and for all by separating
> equivalence and identity into separate operators, and making them
> syntatically consistent, hence my comment of satisfaction in the "Identity
&
> equivalence" thread. However, this opportunity for clarity seems to have
> been thrown if what many of you are telling me is correct, which is that
>
> class X
> {
> }
>
> X    y = . . .;
>
> void f(X x)
> {
>   if(x == y)
>
> will cause an access violation if x or y are null. It makes a nonsense of
> the attempt to deal with the issue. If this cannot be fixed with == (and
> ===) syntax, then we're probably better off removing these operators from
> the language and forcing use of Object.equals() and Object.equalsRef().
For
> my part, though, that'd be gross. So:
>
> It seems that we must find a way to sensibly incorporate === in ==. We
could
> engineer the compiler, or mandate the class author, to incorporate
identity
> checks in equivalence comparisons. However, there are circumstances in
which
> the call to === would be superfluous, reducing efficiency.
>
> afaics, we need to cater for the following scenarios
>
> if(x == y)
>
> (i) x and y are non-null
> (ii) x is null
> (iii) y is null
> (iv) x and y are null
>
> the identity comparison _must_ be involved in == for (ii) and (iv), and
> probably for (iii). I suggest that classes have two comparison functions,
> both static, called eq() and eqi().
>
> eq() compares two references for equality. It may assume that both
> references are guaranteed non-null. If eq() is not supported by a class,
the
> compiler provides "a bit compare of the contents of the two structs is
done
> to determine equality or inequality" as per the current fashion.
>
> eqi() checks for references for equality, mindful of identity. It checks
> whether either or both of the references are null, and if they are not it
> returns the result of calling eq() on them. The default eqi()
implementation
> would be
>
> class X
> {
>   static int eqi(X lhs, X rhs)
>   {
>     if(lhs === rhs) // Identity comparison
>     {
>       return true; // Same instance, therefore equal
>     }
>     else if(lhs === null &&
>              rhs === null)
>     {
>       0; // One, but not both (because of check above), is null, so not
> equal
>     }
>     else
>     {
>       return X.eq(lhs, rhs); // Both non-null, do equivalence comparison
>   }
>
> The author of a class can provide
>
>  - neither
>  - eq() only - the usual case
>  - eqi() only
>  - eq() and eqi()
>
> (I can't imagine a reason for either of the last two, but one never knows
> ...)
>
> It would be *entirely* up to the compiler as to whether it translates x ==
y
> as a call to X.eqi(x, y) or to X.eqi(x, y), presumably based on what it
> knows about the context of the two references. This means that any
> user-defined implementations of eq() and eqi() should be written with this
> in mind, presumably meaning that eqi() would do anything other than the
> default as shown above, apart from logging or other benign things.
>
> Given static eq() and static eqi() we would no longer have non-static eq()
> as a special function, and could likely get rid of it. (I haven't thought
> about that part yet.)
>
> Sound ok?
>
> Comments appreciated. I certainly can't see how we can have access
> violations when using == in expressions!!


   Perhaps we should try to see this with D colored glasses. If I say this:


boolean foo(Object bar, Object baz)
in {
   assert(bar !== null);
   assert(baz !== null);
} body {
   return bar == baz;
}


   I "know" that it is correct. The contract defines that only when bar and
baz are non-null something sensible will happen. If they're null foo can do
anything it wants to (usually just throw an assertion error, but if
contracts are disabled evil things can happen). OTOH if I say this:


boolean foo(Object bar, Object baz) {
   return (bar === null) ? (bar === baz) : (bar == baz);
}


   I "must" deal with the possibility of getting strange values for bar or
baz, so if I don't think carefully I'm assuming something without any
"proof" (e.g. contracts) about it.
   We know that if "a === b", a can be substituted by b anywhere. Also, we
know that "a === b" implies "a == b". This is pretty much what we can say
about equivalence (I don't even think that equivalence must be symmetric but
I may be alone here). This can be expressed in terms of contracts in
Object.eq(Object):


public bit eq(Object other)
in {
   assert(other !== null);
} out(result) {
   if (this === other) {
       assert(result);
   }
   if (null === other) {
// an evil subclass may relax the precondition, but nothing should be eq to
null
       assert(!result);
   }
}


   Working on a language with contracts requires some changes of habit. If
we did something using defensive programming (e.g. accepting nulls and
returning false) now we should use offensive programming (e.g. reject it as
a programming error using contracts). Smalltalk has a Nil object
representing null values that responds to every method with
doesNotUnderstand (similar to NullPointerException or access violation), but
you can make it answer to any method with a Nil value, propagating the
nullness. It may be nice to have an answer that leads to less checks and
simpler code (google for Null Object Pattern), but it can propagate errors
silently, a la NaN in floating point operations. IMO calling "==" is just
sugar for calling "eq", so default restrictions apply too.
   I want to make clear that this mistake is common. Perhaps 1/3 of my bugs
in D are due to using "==" on null variables, instead of the correct
checks/contracts, but the solution isn't make this bug magically disappear
and silently introduce others. We must change our thinking habits when using
D and make heavy use of contracts. While I agree that checking for "null"
may be tedious, it's the D way of doing things (Eiffel works like this too:
their libraries are full of "require other_not_void: other /= Void"). The
Nice language ( http://nice.sourceforge.net ) has a better solution, letting
null values only in types marked with "?" and requiring explicit checks.
Perhaps I should write "Null pointers considered harmful" someday ;-)


>
> Matthew
>
[snip]


   Best regards,
   Daniel Yokomiso.

"I'm less of a neurotic perfectionist than I was.  But I don't think that
anyone who has done good work in their life isn't a perfectionist. You have
to be."
- John Cleese


---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.463 / Virus Database: 262 - Release Date: 18/3/2003
« First   ‹ Prev
1 2 3 4 5
Top | Discussion index | About this forum | D home