www.digitalmars.com         C & C++   DMDScript  

D - null == o?

reply "Matthew Wilson" <dmd synesis.com.au> writes:
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?
Mar 25 2003
next sibling parent reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"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?

 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

 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
Mar 26 2003
parent reply "Matthew Wilson" <dmd synesis.com.au> writes:
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


 a.equals(b).

 Given that, what happens if a is null? NullPointerException, or is there


 "safe-this" mechanism, ie. a.this is automatically checked against null?

 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

 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

 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

Mar 26 2003
next sibling parent Andy Friesen <andy ikagames.com> writes:
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.
Mar 26 2003
prev sibling next sibling parent Burton Radons <loth users.sourceforge.net> writes:
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))
Mar 26 2003
prev sibling next sibling parent reply Benji Smith <Benji_member pathlink.com> writes:
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


 a.equals(b).

 Given that, what happens if a is null? NullPointerException, or is there


 "safe-this" mechanism, ie. a.this is automatically checked against null?

 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

 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

 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


Mar 27 2003
next sibling parent "Matthew Wilson" <dmd synesis.com.au> writes:
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


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


one clueless as to whether any reference comparisons are comparing
equivalence or identity. This also sucks, unless one "trusts" the author


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


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().


my part, though, that'd be gross. So:

It seems that we must find a way to sensibly incorporate === in ==. We


engineer the compiler, or mandate the class author, to incorporate


checks in equivalence comparisons. However, there are circumstances in


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,


compiler provides "a bit compare of the contents of the two structs is


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()


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


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


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


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




as
 a.equals(b).

 Given that, what happens if a is null? NullPointerException, or is




a
 "safe-this" mechanism, ie. a.this is automatically checked against




 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

 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) -





Access violation. It's safer to use a generic equals function that will



 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

 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



Mar 27 2003
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"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.

I'm going to disagree with a couple points. First of all, I'm going to disagree with the notion that an access violation is something bad. I worked for 10 years on MSDOS programs were accessing null pointers scrambled the operating system. I *like* having a program bug promptly generate an exception and stop. Those bugs tend to be easy to find. The hard ones are where your program silently continues on chugging, corrupting data, and obfuscating its origins. It's a good thing when cases not accounted for cause exceptions. The == operator is defined as checking the equivalence of the *contents* of the object references. If the reference is null, it's a program bug and should properly generate an exception. It's analogous to: Foo p = null; p.bar(); which will also generate an exception. A null reference can be a legitimate value in a program, or it could be caused by forgetting to initialize a reference. By coding in an explicit check for null, you're documenting that it can be null. By leaving out such a check, you're implicitly documenting that it cannot be null.
Mar 27 2003
next sibling parent reply "Matthew Wilson" <dmd synesis.com.au> writes:
I agree that access violations have their place, and make use of them on
occasion, but disagree that this is one.

D, like C# and Java, "pretends" that a pointer to an instance is a "thing"
that can be treated like the instance itself. This is evident in the fact
that we wish to, and are able to, apply == to references.

This pretence is a dishonesty, one which is obvious when one considers how
C++ does it. However, when a language does not deal with pointers, the
dishonesty can be benign and useful.

I don't have a problem with this pretence, so long as it's consistent. If
you want objects to be treated "like the ints", then you have to do it
fully. This argument is well worn wrt C++ operator overloading issues, but
applies all round.

When I write code that compares two entities that are int, I don't have to
write

int    i1 = . . .
int    i2 = . . .

if( i1 !== null &&
    i2 !== null &&
    i1 == i2)
{

That would be ludicrous. So why should I have to do it with objects? Object
references should either consistently behave as built-ins in expressions, or
they should be completely different and require different syntax. This is
like having one's syntactic convenience without responsibility for caring
for the syntax. If we can't have that responsibility, then we should eschew
the syntactic convenience and just accept that we have to use Object method
calls for equivalence comparison and, in that case, for identity also.
Either make == and === work in a thoroughly consistent way, or not have them
at all.

Furthermore, since D has templates - you'll have to bear with me on this
one, as I'm a real neophyte with D's template syntax - is it not likely that
we'd have a template that would work with built-in types and with object
references. How would this work? Would we have to specialise on the
built-ins? (What's the point of the template, in that case?) Would we have
checks against null for the built-ins? (I can't imagine this'd compile,
would it?) Would we be forced to write it assuming that the references would
be non-null, since we would not be able to check for that? It all sounds
grim.

The Java situation is totally stupid, but at least it's obvious, and one
quickly gets used to it (though I still forget when I'm flitting between
languages). C#'s optional operator overloading is seemingly less stupid, and
_far_ more convenient, but it's all the more dangerous because it's
partially hidden. What you're saying for D seems an even deeper and more
insidious version, whereby it is more sensible and seemingly useful than
both Java and C#, but not quite sensible enough, and therefore more
dangerous than either of them.

Major wart. Sorry.

"Walter" <walter digitalmars.com> wrote in message
news:b60uqc$7pi$1 digitaldaemon.com...
 "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.

I'm going to disagree with a couple points. First of all, I'm going to disagree with the notion that an access violation is something bad. I

 for 10 years on MSDOS programs were accessing null pointers scrambled the
 operating system. I *like* having a program bug promptly generate an
 exception and stop. Those bugs tend to be easy to find. The hard ones are
 where your program silently continues on chugging, corrupting data, and
 obfuscating its origins. It's a good thing when cases not accounted for
 cause exceptions.

 The == operator is defined as checking the equivalence of the *contents*

 the object references. If the reference is null, it's a program bug and
 should properly generate an exception. It's analogous to:

     Foo p = null;
     p.bar();

 which will also generate an exception.

 A null reference can be a legitimate value in a program, or it could be
 caused by forgetting to initialize a reference. By coding in an explicit
 check for null, you're documenting that it can be null. By leaving out

 a check, you're implicitly documenting that it cannot be null.

Mar 28 2003
next sibling parent reply Bill Cox <bill viasic.com> writes:
Ok, I'll kick in with a very very old point of view on the topic that 
dates back to the beginning of time...

Operator overloading should not be implemented as methods.  They should 
be implemented as overloaded functions at the module level.  Then, this 
whole problem goes away.  Besides, mathematically, a binary operator's 
operands have equal status.  Why pass one to a method of the other? 
It's just a stupid trick to try to avoid writing module-level routines.

The fact that we can't even define the == operator without worrying 
about the LEFT operand being null (but not the right) clearly shows the 
flaw in C++ style operator overloading.

Bill
Mar 28 2003
next sibling parent reply Patrick Down <Patrick_member pathlink.com> writes:
In article <3E84520F.8080809 viasic.com>, Bill Cox says...
Ok, I'll kick in with a very very old point of view on the topic that 
dates back to the beginning of time...

Operator overloading should not be implemented as methods.  They should 
be implemented as overloaded functions at the module level.  Then, this 
whole problem goes away.  Besides, mathematically, a binary operator's 
operands have equal status.  Why pass one to a method of the other? 
It's just a stupid trick to try to avoid writing module-level routines.

The fact that we can't even define the == operator without worrying 
about the LEFT operand being null (but not the right) clearly shows the 
flaw in C++ style operator overloading.

Bill

I think you are right. It really makes more sense for the operator overloading functions to be module level. From a historical D perspective, I think that the reason they were implemented as member fuctions instead of module level functions was the issue of accessing private class members. This was before everything in a module had public access. Now that this is not an issue any more I would certainly be the first to vote for them to be changed.
Mar 28 2003
parent Andy Friesen <andy ikagames.com> writes:
Patrick Down wrote:
 In article <3E84520F.8080809 viasic.com>, Bill Cox says...
 
Ok, I'll kick in with a very very old point of view on the topic that 
dates back to the beginning of time...

Operator overloading should not be implemented as methods.  They should 
be implemented as overloaded functions at the module level.  Then, this 
whole problem goes away.  Besides, mathematically, a binary operator's 
operands have equal status.  Why pass one to a method of the other? 
It's just a stupid trick to try to avoid writing module-level routines.

The fact that we can't even define the == operator without worrying 
about the LEFT operand being null (but not the right) clearly shows the 
flaw in C++ style operator overloading.

Bill

I think you are right. It really makes more sense for the operator overloading functions to be module level. From a historical D perspective, I think that the reason they were implemented as member fuctions instead of module level functions was the issue of accessing private class members. This was before everything in a module had public access. Now that this is not an issue any more I would certainly be the first to vote for them to be changed.

The current setup also allows one to make the comparison method virtual. (I'm not sure how useful that is, but it's there)
Mar 28 2003
prev sibling next sibling parent reply "Sean L. Palmer" <seanpalmer directvinternet.com> writes:
You have got to the root of the problem, Bill.

There is no way in D to do an overloaded operator without having a class
member override some specially named member function.  Thus no module-scope
("free") operators are possible.

You're wrong about C++.  It has these.  They are good.  Preferred in fact to
making the binary operator part of the class.

The only reason you'd want a binary operator to be part of a class is if it
needed access to some private section.  In C++ you use friend for this;  in
D you put the operators in the same module as the class.  (but what if the
operator needs private access to *two* classes that are in different
modules?)

We should be able to define free operators:

// example free operator overload

BigInt operator + (BigInt a, BigInt b)
{
    return BigInt.add(a, b);
}

As you can tell I also dislike the current D syntax for declaring operators.
You have to use some specially named member function currently, which means
you have to remember what the name is for each operator you may want to
overload.  I prefer the direct approach;  If I want an operator I do not
want it to have a name.  It just clutters up the namespace and litters it
with landmines;  for instance the above example wouldn't work because member
add is treated specially and would have already overloaded the + operator.

There are some unanswered questions in the D design.

Sean

"Bill Cox" <bill viasic.com> wrote in message
news:3E84520F.8080809 viasic.com...
 Ok, I'll kick in with a very very old point of view on the topic that
 dates back to the beginning of time...

 Operator overloading should not be implemented as methods.  They should
 be implemented as overloaded functions at the module level.  Then, this
 whole problem goes away.  Besides, mathematically, a binary operator's
 operands have equal status.  Why pass one to a method of the other?
 It's just a stupid trick to try to avoid writing module-level routines.

 The fact that we can't even define the == operator without worrying
 about the LEFT operand being null (but not the right) clearly shows the
 flaw in C++ style operator overloading.

 Bill

Mar 28 2003
next sibling parent Bill Cox <bill viasic.com> writes:
I have to agree with all comments by Sean, Andy, and Patrick.  Like 
Sean, I'd like a "free" operator overloading mechanism, and the syntax 
he's showing looks fine to me.

I guess if we had a "free" version, D would still need the method based 
version.  It fits Walter's vision of C++ programmers feeling very 
comfortable in D.

Walter, would using the actual operator rather than an equivalent 
function name complicate D?

Bill

Sean L. Palmer wrote:
 You have got to the root of the problem, Bill.
 
 There is no way in D to do an overloaded operator without having a class
 member override some specially named member function.  Thus no module-scope
 ("free") operators are possible.
 
 You're wrong about C++.  It has these.  They are good.  Preferred in fact to
 making the binary operator part of the class.
 
 The only reason you'd want a binary operator to be part of a class is if it
 needed access to some private section.  In C++ you use friend for this;  in
 D you put the operators in the same module as the class.  (but what if the
 operator needs private access to *two* classes that are in different
 modules?)
 
 We should be able to define free operators:
 
 // example free operator overload
 
 BigInt operator + (BigInt a, BigInt b)
 {
     return BigInt.add(a, b);
 }
 
 As you can tell I also dislike the current D syntax for declaring operators.
 You have to use some specially named member function currently, which means
 you have to remember what the name is for each operator you may want to
 overload.  I prefer the direct approach;  If I want an operator I do not
 want it to have a name.  It just clutters up the namespace and litters it
 with landmines;  for instance the above example wouldn't work because member
 add is treated specially and would have already overloaded the + operator.
 
 There are some unanswered questions in the D design.
 
 Sean> "Bill Cox" <bill viasic.com> wrote in message
 news:3E84520F.8080809 viasic.com...
 
Ok, I'll kick in with a very very old point of view on the topic that
dates back to the beginning of time...

Operator overloading should not be implemented as methods.  They should
be implemented as overloaded functions at the module level.  Then, this
whole problem goes away.  Besides, mathematically, a binary operator's
operands have equal status.  Why pass one to a method of the other?
It's just a stupid trick to try to avoid writing module-level routines.

The fact that we can't even define the == operator without worrying
about the LEFT operand being null (but not the right) clearly shows the
flaw in C++ style operator overloading.

Bill


Mar 28 2003
prev sibling parent reply "Matthew Wilson" <dmd synesis.com.au> writes:
Agreed.

Although I can understand that Walter may say it's easier for the compiler
to parse a named function than the C++'s operator syntax.

If that's the objection, then why not have the python model? There are a
limited number of operators in the character set, all of which can be named.

Your example would then be

BigInt __op_add__ (BigInt a, BigInt b)
{



"Sean L. Palmer" <seanpalmer directvinternet.com> wrote in message
news:b624r1$14db$1 digitaldaemon.com...
 You have got to the root of the problem, Bill.

 There is no way in D to do an overloaded operator without having a class
 member override some specially named member function.  Thus no

 ("free") operators are possible.

 You're wrong about C++.  It has these.  They are good.  Preferred in fact

 making the binary operator part of the class.

 The only reason you'd want a binary operator to be part of a class is if

 needed access to some private section.  In C++ you use friend for this;

 D you put the operators in the same module as the class.  (but what if the
 operator needs private access to *two* classes that are in different
 modules?)

 We should be able to define free operators:

 // example free operator overload

 BigInt operator + (BigInt a, BigInt b)
 {
     return BigInt.add(a, b);
 }

 As you can tell I also dislike the current D syntax for declaring

 You have to use some specially named member function currently, which

 you have to remember what the name is for each operator you may want to
 overload.  I prefer the direct approach;  If I want an operator I do not
 want it to have a name.  It just clutters up the namespace and litters it
 with landmines;  for instance the above example wouldn't work because

 add is treated specially and would have already overloaded the + operator.

 There are some unanswered questions in the D design.

 Sean

 "Bill Cox" <bill viasic.com> wrote in message
 news:3E84520F.8080809 viasic.com...
 Ok, I'll kick in with a very very old point of view on the topic that
 dates back to the beginning of time...

 Operator overloading should not be implemented as methods.  They should
 be implemented as overloaded functions at the module level.  Then, this
 whole problem goes away.  Besides, mathematically, a binary operator's
 operands have equal status.  Why pass one to a method of the other?
 It's just a stupid trick to try to avoid writing module-level routines.

 The fact that we can't even define the == operator without worrying
 about the LEFT operand being null (but not the right) clearly shows the
 flaw in C++ style operator overloading.

 Bill


Mar 28 2003
parent reply "Walter" <walter digitalmars.com> writes:
The reason the operator overloads are class members is so they can be
virtual.

It might be a good idea to do an "op" prefix to all the operator overloads.

"Matthew Wilson" <dmd synesis.com.au> wrote in message
news:b638ou$1v3k$1 digitaldaemon.com...
 Agreed.

 Although I can understand that Walter may say it's easier for the compiler
 to parse a named function than the C++'s operator syntax.

 If that's the objection, then why not have the python model? There are a
 limited number of operators in the character set, all of which can be

 Your example would then be

 BigInt __op_add__ (BigInt a, BigInt b)
 {

May 16 2003
next sibling parent reply "C. Sauls" <ibisbasenji yahoo.com> writes:
 It might be a good idea to do an "op" prefix to all the operator

I agree.. though I wonder, is operator-overloading meant to be implementation-specific? If so, no problem. If not, then might it be better to have a slightly more explicit system for overloading? I.e., an overloading keyword such as C++'s Class Class::operator+(Class obj) { ... } In D we could perhaps use something like Class operator add(Class obj) { //overload + ... }
May 17 2003
parent "Walter" <walter digitalmars.com> writes:
I didn't like the "operator+" notation because of the problem with the
reverse operators.

"C. Sauls" <ibisbasenji yahoo.com> wrote in message
news:ba5jnn$d47$1 digitaldaemon.com...
 It might be a good idea to do an "op" prefix to all the operator

I agree.. though I wonder, is operator-overloading meant to be implementation-specific? If so, no problem. If not, then might it be better to have a slightly more explicit system for overloading? I.e., an overloading keyword such as C++'s Class Class::operator+(Class obj) { ... } In D we could perhaps use something like Class operator add(Class obj) { //overload + ... }

May 18 2003
prev sibling parent reply Ilya Minkov <midiclub 8ung.at> writes:
Walter wrote:
 It might be a good idea to do an "op" prefix to all the operator overloads.

As you mention that... maybe. How about another prefix ("oprev"?), which would allow to reverse parameter order for infix operators? Then an operator resolver would have to look both in the "left" class (for "op...") as in the "right" one (for "oprev..."), and complain if there's a collision. The motivation for this would be the ability to add new "mathematic" classes to the hierarchy without having to resort to module-wide operator definitions, which are usually done in C++. -i.
May 17 2003
parent reply "Giancarlo Bellido" <vicentico1 hotmail.com> writes:
Personally, I think that operator overloading should be something like in
C++.
why?? because it is nicer, easy to remember, and people reading the code
would know that it is actually an operator.

another thing, there is no way to create operators outside the class, like
you
do for the istream or ostream objects in c++.

giancarlo

"Ilya Minkov" <midiclub 8ung.at> wrote in message
news:ba5kre$e25$1 digitaldaemon.com...
 Walter wrote:
 It might be a good idea to do an "op" prefix to all the operator


 As you mention that... maybe.

 How about another prefix ("oprev"?), which would allow to reverse
 parameter order for infix operators? Then an operator resolver would
 have to look both in the "left" class (for "op...") as in the "right"
 one (for "oprev..."), and complain if there's a collision.

 The motivation for this would be the ability to add new "mathematic"
 classes to the hierarchy without having to resort to module-wide
 operator definitions, which are usually done in C++.

 -i.

May 17 2003
parent reply "Sean L. Palmer" <palmer.sean verizon.net> writes:
I agree.  I like C++ operator overloading better.  It bugs me that if I want
a '+' I have to type 'add';  I really cannot see the problem with a WYSIWYG
system.  It's not that much more work in the parser, is it?

The inability to make free operators is really limiting.  It means you can't
make operator overloads unless you have full control to the source of at
least one of the classes involved, but in many cases you may not own either
of the classes but may still want to provide an overloaded operator for some
reason. (What if someone invents a cool STL trick that works like that, in
the future...?  C++ will be able to do it, D won't.)  There doesn't seem to
be any real reason why not, except that this way allows operators to be
virtual, which I say is hogwash since you could always define your operator
to call a virtual method of the class itself if you choose to.

Sean

"Giancarlo Bellido" <vicentico1 hotmail.com> wrote in message
news:ba6l78$1gro$1 digitaldaemon.com...
 Personally, I think that operator overloading should be something like in
 C++.
 why?? because it is nicer, easy to remember, and people reading the code
 would know that it is actually an operator.

 another thing, there is no way to create operators outside the class, like
 you
 do for the istream or ostream objects in c++.

 giancarlo

 "Ilya Minkov" <midiclub 8ung.at> wrote in message
 news:ba5kre$e25$1 digitaldaemon.com...
 Walter wrote:
 It might be a good idea to do an "op" prefix to all the operator


 As you mention that... maybe.

 How about another prefix ("oprev"?), which would allow to reverse
 parameter order for infix operators? Then an operator resolver would
 have to look both in the "left" class (for "op...") as in the "right"
 one (for "oprev..."), and complain if there's a collision.

 The motivation for this would be the ability to add new "mathematic"
 classes to the hierarchy without having to resort to module-wide
 operator definitions, which are usually done in C++.

 -i.


May 18 2003
next sibling parent reply Ilya Minkov <midiclub 8ung.at> writes:
Sean L. Palmer wrote:
 I agree.  I like C++ operator overloading better.  It bugs me that if I want
 a '+' I have to type 'add';  I really cannot see the problem with a WYSIWYG
 system.  It's not that much more work in the parser, is it?

It shall make writing other tools which process D code more difficult. Not the EOW though. But i frankly don't know why add bothers you. You can grep after it. You know where to search for an operator definition for a reference. Unlike freestanding operator defintions, which can be anywhere.
 The inability to make free operators is really limiting.  It means you can't
 make operator overloads unless you have full control to the source of at
 least one of the classes involved, but in many cases you may not own either
 of the classes but may still want to provide an overloaded operator for some
 reason.

What the h*** that reason would be? Bad original library design? Besides: you don't have to be in control of source to add operators to existing classes. Simply subclass. There also is a technique which allows you to do that to structs.
 (What if someone invents a cool STL trick that works like that, in
 the future...?  C++ will be able to do it, D won't.)  There doesn't seem to
 be any real reason why not, except that this way allows operators to be
 virtual, which I say is hogwash since you could always define your operator
 to call a virtual method of the class itself if you choose to.

Operators *have* to be virtual, else you would be redefining them to call virtual methods for each of descendants of some basic mathematic class. Think OO people. I feel like changing to Sather. -i.
May 18 2003
next sibling parent reply "Giancarlo Bellido" <vicentico1 hotmail.com> writes:
ok, what about declaring the operator like a function without the operator
keyword,
like:

    classname + (classname2 r);

and about free operators i think they are important because they give more
control
over types, because there is no way to add functionality to a class like the
istream,
ostream class in c++, the only way by now is modifying the source.

giancarlo

"Ilya Minkov" <midiclub 8ung.at> wrote in message
news:ba8954$je6$1 digitaldaemon.com...
 Sean L. Palmer wrote:
 I agree.  I like C++ operator overloading better.  It bugs me that if I


 a '+' I have to type 'add';  I really cannot see the problem with a


 system.  It's not that much more work in the parser, is it?

It shall make writing other tools which process D code more difficult. Not the EOW though. But i frankly don't know why add bothers you. You can grep after it. You know where to search for an operator definition for a reference. Unlike freestanding operator defintions, which can be anywhere.
 The inability to make free operators is really limiting.  It means you


 make operator overloads unless you have full control to the source of at
 least one of the classes involved, but in many cases you may not own


 of the classes but may still want to provide an overloaded operator for


 reason.

What the h*** that reason would be? Bad original library design? Besides: you don't have to be in control of source to add operators to existing classes. Simply subclass. There also is a technique which allows you to do that to structs.
 (What if someone invents a cool STL trick that works like that, in
 the future...?  C++ will be able to do it, D won't.)  There doesn't seem


 be any real reason why not, except that this way allows operators to be
 virtual, which I say is hogwash since you could always define your


 to call a virtual method of the class itself if you choose to.

Operators *have* to be virtual, else you would be redefining them to call virtual methods for each of descendants of some basic mathematic

 Think OO people. I feel like changing to Sather.

 -i.

May 18 2003
next sibling parent Ilya Minkov <midiclub 8ung.at> writes:
Giancarlo Bellido wrote:
 ok, what about declaring the operator like a function without the
 operator keyword, like:
 
 classname + (classname2 r);

Yikes, UGLY! The problem with it is that you cannot grep for it. In C++ you would search for "operator" or "operator+", in Walter's proposal you can search for "op" or "opadd", but yours is simply painful.
 and about free operators i think they are important because they give
 more control over types, because there is no way to add functionality
 to a class like the istream, ostream class in c++, the only way by
 now is modifying the source.

Consider: do you really need to add functioality to these classes? If you feel like adding more classes to the iostream hierarchy, "reverse" operator definitions would allow that. If you think SomeLibraryClass is not advanced enough... go ahead and subclass it into MySomeLibraryClass. Then you would be able not only to add operators, but also add new methods or modify existing ones, in a consistent manner. Adding operators just to circumvent this is a mere obfuscation. -i.
May 18 2003
prev sibling parent Andy Friesen <andy ikagames.com> writes:
Giancarlo Bellido wrote:
 ok, what about declaring the operator like a function without the operator
 keyword,
 like:
 
     classname + (classname2 r);
 
 and about free operators i think they are important because they give more
 control
 over types, because there is no way to add functionality to a class like the
 istream,
 ostream class in c++, the only way by now is modifying the source.
 
 giancarlo
 

Except for shl_r and shr_r :) class MyClass { Stream shl_r(Stream s) { doStuffWith(s); return s; } }
May 18 2003
prev sibling next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Ilya Minkov" <midiclub 8ung.at> wrote in message
news:ba8954$je6$1 digitaldaemon.com...
 It shall make writing other tools which process D code more difficult.
 Not the EOW though. But i frankly don't know why add bothers you. You
 can grep after it. You know where to search for an operator definition
 for a reference. Unlike freestanding operator defintions, which can be
 anywhere.

To me, being able to grep for it is a big deal. I also know where to look for the operator overloads for a particular class. I don't with global operator overloads. It's like overloading the global ::new in C++. It seemed like a good idea at first, but after a while I excorcised all of that from my code and switched to local operator new's.
May 18 2003
parent "Matthew Wilson" <matthew stlsoft.org> writes:
I think the python style - widely recognised and greppable names - is
superior to the C++ one  - operator /+() ... - even though it is less terse.

One of the reasons for the C++ operator style was due to Bjarne Stroustrup's
desire/need to introduce as few keywords as possible, as he had to upset the
C community to the minimum degree in the early days of C++. (Consider = 0
for pure virtuals, class for typename in template parameter declarations,
etc. etc.)

Since D does not have these same constraints, let's do what's sensible both
for compiler writers and maintenance programmers. Unambiguous symbols rule
here.
May 20 2003
prev sibling parent "Matthew Wilson" <matthew stlsoft.org> writes:
 Think OO people. I feel like changing to Sather.

Why should we think OO? One of the reasons that C++ is fantastic (and widely used) is that it supports many different programming paradigms, and prescribes none. In my opinion OO is *vastly* overrated. D has the potential to leave .NET & Java in the dust by following C++'s lead in supporting multiple paradigms. Frankly, if we're just going to go OO, then I cannot think of a reason why I wouldn't just use C# - better IDDE, bigger libraries, more likelihood of having your work accepted by commercial clients blah, blah, blah. Do not think OO! Think, and support, whatever programming paradigm (or paradigms) suitable to your programming task.
May 20 2003
prev sibling next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <palmer.sean verizon.net> wrote in message
news:ba7jsb$2kdn$1 digitaldaemon.com...
 I agree.  I like C++ operator overloading better.  It bugs me that if I

 a '+' I have to type 'add';  I really cannot see the problem with a

 system.  It's not that much more work in the parser, is it?

The problem comes with the reverse operators, such as divr.
 The inability to make free operators is really limiting.  It means you

 make operator overloads unless you have full control to the source of at
 least one of the classes involved, but in many cases you may not own

 of the classes but may still want to provide an overloaded operator for

 reason. (What if someone invents a cool STL trick that works like that, in
 the future...?  C++ will be able to do it, D won't.)  There doesn't seem

 be any real reason why not, except that this way allows operators to be
 virtual, which I say is hogwash since you could always define your

 to call a virtual method of the class itself if you choose to.

I don't like the C++ approach because: 1) its inherent asymmetry in how you do (a op b) versus (b op a). 2) its ability to produce operator overloads that have bizarrely different semantics with the operator, such as the iostream << and >>. 3) you need to write, in general, twice as much code in C++ to define a new class with operators than you do in D. 4) I have difficulty seeing the merit in global operator overloads. To me, they always looked like a hack because you couldn't do (b op a) with the operator+ syntax. Not that I have a strong opinion about it, or anything <g>.
May 18 2003
next sibling parent reply "Sean L. Palmer" <palmer.sean verizon.net> writes:
"Walter" <walter digitalmars.com> wrote in message
news:ba9i5m$2l4g$2 digitaldaemon.com...
 "Sean L. Palmer" <palmer.sean verizon.net> wrote in message
 news:ba7jsb$2kdn$1 digitaldaemon.com...
 I agree.  I like C++ operator overloading better.  It bugs me that if I

 a '+' I have to type 'add';  I really cannot see the problem with a

 system.  It's not that much more work in the parser, is it?

The problem comes with the reverse operators, such as divr.

What problem? float operator-(int a, float b) { return float(a) - b; } float operator-(float a, int b) { return a - float(b); }
 The inability to make free operators is really limiting.  It means you

 make operator overloads unless you have full control to the source of at
 least one of the classes involved, but in many cases you may not own

 of the classes but may still want to provide an overloaded operator for

 reason. (What if someone invents a cool STL trick that works like that,


 the future...?  C++ will be able to do it, D won't.)  There doesn't seem

 be any real reason why not, except that this way allows operators to be
 virtual, which I say is hogwash since you could always define your

 to call a virtual method of the class itself if you choose to.

I don't like the C++ approach because: 1) its inherent asymmetry in how you do (a op b) versus (b op a).

see above.
 2) its ability to produce operator overloads that have bizarrely different
 semantics with the operator, such as the iostream << and >>.

This is a religious issue, which makes me think this will never be solved in D to my liking.
 3) you need to write, in general, twice as much code in C++ to define a

 class with operators than you do in D.

Example?
 4) I have difficulty seeing the merit in global operator overloads. To me,
 they always looked like a hack because you couldn't do (b op a) with the
 operator+ syntax.

You've used that argument 3 times now.
 Not that I have a strong opinion about it, or anything <g>.

None of us do. ;) At first I was all about making my operators class members in C++, but recently, a few months ago, I had a change of heart because I was writing some math classes and it started getting really ugly when all operators were members. Particularly, it became problematic to decide which class the operator should actually belong to. In some cases, the choice seems completely and totally arbitrary, which indicates to me that there is no good reason to place the operator in either class, but that it should in fact be global (ok, module) scope. My code got alot easier to read after that. Well, that's good enough argument for me. Sean
May 19 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <palmer.sean verizon.net> wrote in message
news:ba9vfv$e61$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:ba9i5m$2l4g$2 digitaldaemon.com...
 "Sean L. Palmer" <palmer.sean verizon.net> wrote in message
 news:ba7jsb$2kdn$1 digitaldaemon.com...
 I agree.  I like C++ operator overloading better.  It bugs me that if



 want
 a '+' I have to type 'add';  I really cannot see the problem with a

 system.  It's not that much more work in the parser, is it?


float operator-(int a, float b) { return float(a) - b; } float operator-(float a, int b) { return a - float(b); }

That doesn't work if you want to make them virtual.
 2) its ability to produce operator overloads that have bizarrely


 semantics with the operator, such as the iostream << and >>.


 D to my liking.

Perhaps.
 3) you need to write, in general, twice as much code in C++ to define a

 class with operators than you do in D.


In C++, you have to overload both == and !=. In D, one overload handles both. The same for commutative operators - in D, just do one of them, in C++, you must do both. Things are even more wordy in C++ if you want to do <, <=, >, >=. You frequently have to write 8 functions in C++, in D just one.
 Not that I have a strong opinion about it, or anything <g>.


<g>
May 28 2003
parent reply "Sean L. Palmer" <palmer.sean verizon.net> writes:
"Walter" <walter digitalmars.com> wrote in message
news:bb46ij$2nd9$1 digitaldaemon.com...
 "Sean L. Palmer" <palmer.sean verizon.net> wrote in message
 news:ba9vfv$e61$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:ba9i5m$2l4g$2 digitaldaemon.com...
 "Sean L. Palmer" <palmer.sean verizon.net> wrote in message
 news:ba7jsb$2kdn$1 digitaldaemon.com...
 I agree.  I like C++ operator overloading better.  It bugs me that




 I
 want
 a '+' I have to type 'add';  I really cannot see the problem with a

 system.  It's not that much more work in the parser, is it?


float operator-(int a, float b) { return float(a) - b; } float operator-(float a, int b) { return a - float(b); }

That doesn't work if you want to make them virtual.

What if I don't care if they're virtual? Besides as has been pointed out you can still chain to a virtual member function if you want that.
 3) you need to write, in general, twice as much code in C++ to define



 new
 class with operators than you do in D.


In C++, you have to overload both == and !=. In D, one overload handles both. The same for commutative operators - in D, just do one of them, in C++, you must do both. Things are even more wordy in C++ if you want to do <, <=, >, >=. You frequently have to write 8 functions in C++, in D just one.

What does this have to do with the operator being a member function or not? In fact what does it have to do with the naming scheme? You could easily make default operators in C++; Boost even does it within the existing language. I don't understand the current stance on prohibition of nonmember operators. I don't see any good reasoning for it. But there are good reasons why people want loose operators. Often the operation doesn't really "belong" in either class. Look at Java. They made it so every function has to be a class member. Now people have to make classes they don't need. At least let us inherit from basic types: struct posint : public int { } Sean
May 29 2003
next sibling parent reply Mark T <Mark_member pathlink.com> writes:
I don't understand the current stance on prohibition of nonmember operators.
I don't see any good reasoning for it.  But there are good reasons why
people want loose operators.  Often the operation doesn't really "belong" in
either class.

Look at Java.  They made it so every function has to be a class member.  Now
people have to make classes they don't need.

I agree, fake classes to "encapsulate" all functions like Java is bad. Read http://www.cuj.com/documents/s=8042/cuj0002meyers/ for a related idea
May 29 2003
parent "Sean L. Palmer" <palmer.sean verizon.net> writes:
It seems like it would be a good idea to unify all function and method call
syntax.

But how?  Go back to C?  Occasionally it's kind of nice to know which
parameter is the "main" parameter (called this in C++). Several languages do
this, but I'm not sure how it works out because I haven't tried it recently.
This is how everything worked in C.

Maybe access control, in general, is bad.  Perhaps data and function hiding
should be done by some other mechanism, such as modules and scopes and
namespaces, and not so much by inheritance.

This is a fundamental issue, one that would drastically alter a language
design.

Sean

"Mark T" <Mark_member pathlink.com> wrote in message
news:bb6817$20dc$1 digitaldaemon.com...
I don't understand the current stance on prohibition of nonmember


I don't see any good reasoning for it.  But there are good reasons why
people want loose operators.  Often the operation doesn't really "belong"


either class.

Look at Java.  They made it so every function has to be a class member.


people have to make classes they don't need.

I agree, fake classes to "encapsulate" all functions like Java is bad. Read http://www.cuj.com/documents/s=8042/cuj0002meyers/ for a related idea

Jun 01 2003
prev sibling parent "Matthew Wilson" <matthew stlsoft.org> writes:
 I don't understand the current stance on prohibition of nonmember

 I don't see any good reasoning for it.

Agreed.
 But there are good reasons why
 people want loose operators.  Often the operation doesn't really "belong"

 either class.

Agreed.
 Look at Java.  They made it so every function has to be a class member.

 people have to make classes they don't need.

Agreed. So, in summary, agreed!
May 29 2003
prev sibling parent "Matthew Wilson" <matthew stlsoft.org> writes:
"Walter" <walter digitalmars.com> wrote in message
news:ba9i5m$2l4g$2 digitaldaemon.com...
 "Sean L. Palmer" <palmer.sean verizon.net> wrote in message
 news:ba7jsb$2kdn$1 digitaldaemon.com...
 I agree.  I like C++ operator overloading better.  It bugs me that if I

 a '+' I have to type 'add';  I really cannot see the problem with a

 system.  It's not that much more work in the parser, is it?

The problem comes with the reverse operators, such as divr.
 The inability to make free operators is really limiting.  It means you

 make operator overloads unless you have full control to the source of at
 least one of the classes involved, but in many cases you may not own

 of the classes but may still want to provide an overloaded operator for

 reason. (What if someone invents a cool STL trick that works like that,


 the future...?  C++ will be able to do it, D won't.)  There doesn't seem

 be any real reason why not, except that this way allows operators to be
 virtual, which I say is hogwash since you could always define your

 to call a virtual method of the class itself if you choose to.

I don't like the C++ approach because: 1) its inherent asymmetry in how you do (a op b) versus (b op a).

Disagree. This in convenience is well worth the fact that operators are free, rather than members
 2) its ability to produce operator overloads that have bizarrely different
 semantics with the operator, such as the iostream << and >>.

Agree, MASSIVELY. Those chevrons and the iostreams are a wart and a curse
 3) you need to write, in general, twice as much code in C++ to define a

 class with operators than you do in D.

Disagree, same reason as 1)
 4) I have difficulty seeing the merit in global operator overloads. To me,
 they always looked like a hack because you couldn't do (b op a) with the
 operator+ syntax.

Nope. Very useful. Free functions rule
 Not that I have a strong opinion about it, or anything <g>.

May 20 2003
prev sibling parent reply "Matthew Wilson" <matthew stlsoft.org> writes:
Couldn't agree more.


"Sean L. Palmer" <palmer.sean verizon.net> wrote in message
news:ba7jsb$2kdn$1 digitaldaemon.com...
 I agree.  I like C++ operator overloading better.  It bugs me that if I

 a '+' I have to type 'add';  I really cannot see the problem with a

 system.  It's not that much more work in the parser, is it?

 The inability to make free operators is really limiting.  It means you

 make operator overloads unless you have full control to the source of at
 least one of the classes involved, but in many cases you may not own

 of the classes but may still want to provide an overloaded operator for

 reason. (What if someone invents a cool STL trick that works like that, in
 the future...?  C++ will be able to do it, D won't.)  There doesn't seem

 be any real reason why not, except that this way allows operators to be
 virtual, which I say is hogwash since you could always define your

 to call a virtual method of the class itself if you choose to.

 Sean

 "Giancarlo Bellido" <vicentico1 hotmail.com> wrote in message
 news:ba6l78$1gro$1 digitaldaemon.com...
 Personally, I think that operator overloading should be something like


 C++.
 why?? because it is nicer, easy to remember, and people reading the code
 would know that it is actually an operator.

 another thing, there is no way to create operators outside the class,


 you
 do for the istream or ostream objects in c++.

 giancarlo

 "Ilya Minkov" <midiclub 8ung.at> wrote in message
 news:ba5kre$e25$1 digitaldaemon.com...
 Walter wrote:
 It might be a good idea to do an "op" prefix to all the operator


 As you mention that... maybe.

 How about another prefix ("oprev"?), which would allow to reverse
 parameter order for infix operators? Then an operator resolver would
 have to look both in the "left" class (for "op...") as in the "right"
 one (for "oprev..."), and complain if there's a collision.

 The motivation for this would be the ability to add new "mathematic"
 classes to the hierarchy without having to resort to module-wide
 operator definitions, which are usually done in C++.

 -i.



May 20 2003
parent reply Georg Wrede <Georg_member pathlink.com> writes:
From the specs:

  Arrays that parallel an enum

  The C Way
  ---------

  Consider:
  
         enum COLORS { red, blue, green, max }; 
         char *cstring[max] = {"red", "blue", "green" }; 
  
  This is fairly easy to get right because the number of entries is
  small. But suppose it gets to be fairly large. Then it can get
  difficult to maintain correctly when new entries are added.

  The D Way
  ---------
  
    enum COLORS { red, blue, green }
  
      char cstring[COLORS.max + 1][] = 
      [
          COLORS.red   : "red",
          COLORS.blue  : "blue", 
          COLORS.green : "green",
      ]; 
  
  Not perfect, but better.

Now, having: enum COLORS { red, blue, green } What if we implemented: static char cstring[][] = COLORS; as inserting the names as strings to the string array? They are, after all, known at compile time? Isn't this the most common idiom to use near the definition of enums? It might be overkill to have the enum statement implicitly define a COLORS struct with the strings defined, but isn't this common enough to deserve a special syntax, like: enum COLORS {red, blue, green} : string_access; or static class enum COLORS {red, blue, green}; but I really believe we ought to do something about it. If not for else, the existing syntax requires the colors to be stated two times. This is a c(++) like "catch", isn't it? With "300 items in the list, and counting", it is a good bet that the enums and the strings will get out of sync sooner or later.
May 21 2003
next sibling parent Helmut Leitner <helmut.leitner chello.at> writes:
Georg Wrede wrote:
 
 From the specs:
 
  Arrays that parallel an enum

  The C Way
  ---------

  Consider:

         enum COLORS { red, blue, green, max };
         char *cstring[max] = {"red", "blue", "green" };

  This is fairly easy to get right because the number of entries is
  small. But suppose it gets to be fairly large. Then it can get
  difficult to maintain correctly when new entries are added.

  The D Way
  ---------

    enum COLORS { red, blue, green }

      char cstring[COLORS.max + 1][] =
      [
          COLORS.red   : "red",
          COLORS.blue  : "blue",
          COLORS.green : "green",
      ];

  Not perfect, but better.

Now, having: enum COLORS { red, blue, green } What if we implemented: static char cstring[][] = COLORS; as inserting the names as strings to the string array? They are, after all, known at compile time? Isn't this the most common idiom to use near the definition of enums? It might be overkill to have the enum statement implicitly define a COLORS struct with the strings defined, but isn't this common enough to deserve a special syntax, like: enum COLORS {red, blue, green} : string_access; or static class enum COLORS {red, blue, green}; but I really believe we ought to do something about it. If not for else, the existing syntax requires the colors to be stated two times. This is a c(++) like "catch", isn't it? With "300 items in the list, and counting", it is a good bet that the enums and the strings will get out of sync sooner or later.

Wouldn't it be nicer to have an enum property .hash returning a constant assoziative array allowing the efficient lookup of enum string values: enum COLORS {red, blue, green}; COLORS col; int [char[]] enumhash; enumhash=col.hash; enumhash=COLORS.hash; int val; val=enumhash["red"]; val=COLORS.hash["red"]; val=col.hash["red"]; -- Helmut Leitner leitner hls.via.at Graz, Austria www.hls-software.com
May 21 2003
prev sibling parent "Sean L. Palmer" <palmer.sean verizon.net> writes:
Simply have .toString() propertys on enum members.  If they are never used,
the linker can strip their string tables out.

Sean

"Georg Wrede" <Georg_member pathlink.com> wrote in message
news:bah62i$gpb$1 digitaldaemon.com...
 From the specs:

  Arrays that parallel an enum

  The C Way
  ---------

  Consider:

         enum COLORS { red, blue, green, max };
         char *cstring[max] = {"red", "blue", "green" };

  This is fairly easy to get right because the number of entries is
  small. But suppose it gets to be fairly large. Then it can get
  difficult to maintain correctly when new entries are added.

  The D Way
  ---------

    enum COLORS { red, blue, green }

      char cstring[COLORS.max + 1][] =
      [
          COLORS.red   : "red",
          COLORS.blue  : "blue",
          COLORS.green : "green",
      ];

  Not perfect, but better.

Now, having: enum COLORS { red, blue, green } What if we implemented: static char cstring[][] = COLORS; as inserting the names as strings to the string array? They are, after

 known at compile time?

 Isn't this the most common idiom to use near the definition of enums? It

 be overkill to have the enum statement implicitly define a COLORS struct

 the strings defined, but isn't this common enough to deserve a special

 like:

 enum COLORS {red, blue, green} : string_access;

 or

 static class enum COLORS {red, blue, green};

 but I really believe we ought to do something about it.

 If not for else, the existing syntax requires the colors to be stated two

 This is a c(++) like "catch", isn't it? With "300 items in the list, and
 counting", it is a good bet that the enums and the strings will get out of

 sooner or later.

May 22 2003
prev sibling parent reply "Matthew Wilson" <dmd synesis.com.au> writes:
Bill, I think you're agreeing with what I'm saying, sort of. I suggested
making the eq() and eqi() methods static simply because I'm not aware of a D
mechanism for doing it outside the class, which is obviously the preferred
approach.

I'm not quite sure what you mean wrt C++. In C++ one can define operator ==
as a class member, or as a free function with or without friend access to
the class. In my opinion (and I think it is the widely accepted preference)
free functions without friend access is the preferred approach. Where's the
flaw?


"Bill Cox" <bill viasic.com> wrote in message
news:3E84520F.8080809 viasic.com...
 Ok, I'll kick in with a very very old point of view on the topic that
 dates back to the beginning of time...

 Operator overloading should not be implemented as methods.  They should
 be implemented as overloaded functions at the module level.  Then, this
 whole problem goes away.  Besides, mathematically, a binary operator's
 operands have equal status.  Why pass one to a method of the other?
 It's just a stupid trick to try to avoid writing module-level routines.

 The fact that we can't even define the == operator without worrying
 about the LEFT operand being null (but not the right) clearly shows the
 flaw in C++ style operator overloading.

 Bill

Mar 28 2003
parent Bill Cox <bill viasic.com> writes:
Matthew Wilson wrote:
 Bill, I think you're agreeing with what I'm saying, sort of. I suggested
 making the eq() and eqi() methods static simply because I'm not aware of a D
 mechanism for doing it outside the class, which is obviously the preferred
 approach.
 
 I'm not quite sure what you mean wrt C++. In C++ one can define operator ==
 as a class member, or as a free function with or without friend access to
 the class. In my opinion (and I think it is the widely accepted preference)
 free functions without friend access is the preferred approach. Where's the
 flaw?

You're right. My mistake, I just forgot. For some reason, I haven't seen use of the free version in a while. It does seem like the prefered aproach to me.
 "Bill Cox" <bill viasic.com> wrote in message
 news:3E84520F.8080809 viasic.com...
 
Ok, I'll kick in with a very very old point of view on the topic that
dates back to the beginning of time...

Operator overloading should not be implemented as methods.  They should
be implemented as overloaded functions at the module level.  Then, this
whole problem goes away.  Besides, mathematically, a binary operator's
operands have equal status.  Why pass one to a method of the other?
It's just a stupid trick to try to avoid writing module-level routines.

The fact that we can't even define the == operator without worrying
about the LEFT operand being null (but not the right) clearly shows the
flaw in C++ style operator overloading.

Bill


Mar 28 2003
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Matthew Wilson" <dmd synesis.com.au> wrote in message
news:b61875$epf$1 digitaldaemon.com...
 D, like C# and Java, "pretends" that a pointer to an instance is a "thing"
 that can be treated like the instance itself. This is evident in the fact
 that we wish to, and are able to, apply == to references.

 This pretence is a dishonesty, one which is obvious when one considers how
 C++ does it. However, when a language does not deal with pointers, the
 dishonesty can be benign and useful.

 I don't have a problem with this pretence, so long as it's consistent. If
 you want objects to be treated "like the ints", then you have to do it
 fully. This argument is well worn wrt C++ operator overloading issues, but
 applies all round.

 When I write code that compares two entities that are int, I don't have to
 write

 int    i1 = . . .
 int    i2 = . . .

 if( i1 !== null &&
     i2 !== null &&
     i1 == i2)
 {

 That would be ludicrous. So why should I have to do it with objects?

 references should either consistently behave as built-ins in expressions,

 they should be completely different and require different syntax. This is
 like having one's syntactic convenience without responsibility for caring
 for the syntax. If we can't have that responsibility, then we should

 the syntactic convenience and just accept that we have to use Object

 calls for equivalence comparison and, in that case, for identity also.

I'll argue that value semantics and reference semantics are fundamentally different, despite having a common syntax. I don't think there's any getting away from that. Objects are accessed by reference, not by value, and thinking of them as having value semantics (like ints in your example) just leads to disaster in many more ways than the behavior of == and === (for example, consider passing function arguments by value and by reference and the semantic differences).
 Furthermore, since D has templates - you'll have to bear with me on this
 one, as I'm a real neophyte with D's template syntax - is it not likely

 we'd have a template that would work with built-in types and with object
 references. How would this work? Would we have to specialise on the
 built-ins? (What's the point of the template, in that case?) Would we have
 checks against null for the built-ins? (I can't imagine this'd compile,
 would it?) Would we be forced to write it assuming that the references

 be non-null, since we would not be able to check for that? It all sounds
 grim.

You can specialize with Object, that way all objects goto one template, and everything else goes to another, if it turns out to be a problem.
Mar 28 2003
prev sibling parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"Walter" <walter digitalmars.com> wrote in message
news:b60uqc$7pi$1 digitaldaemon.com...
 "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.

I'm going to disagree with a couple points. First of all, I'm going to disagree with the notion that an access violation is something bad. I

 for 10 years on MSDOS programs were accessing null pointers scrambled the
 operating system. I *like* having a program bug promptly generate an
 exception and stop. Those bugs tend to be easy to find. The hard ones are
 where your program silently continues on chugging, corrupting data, and
 obfuscating its origins. It's a good thing when cases not accounted for
 cause exceptions.

I agree that use of null pointers should generate exceptions but ....
 The == operator is defined as checking the equivalence of the *contents*

 the object references. If the reference is null, it's a program bug and
 should properly generate an exception. It's analogous to:

isn't null equivilant to null ? and if nulls are usually programming errors why then can I append to a null array. (I like the Java ability to have 0 length arrays)
Mar 31 2003
parent reply Ilya Minkov <midiclub 8ung.at> writes:
Mike Wynn wrote:
 isn't null equivilant to null ?

No. Taken C++: mytype a, b; mytype & A = a; mytype & B = b; // ... if (A == B) {} // here both A and B are first dereferenced, then compared. If you handle references, how are you going to compare them, when dereferencing one of them already causes an exception? To compare adress values of references, you would want to say: if (&A == &B) {} Or in D with objects, simply if (A === B) {} I simply can't see what you don't buy about it. -i.
Apr 09 2003
parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"Ilya Minkov" <midiclub 8ung.at> wrote in message
news:b71res$1541$1 digitaldaemon.com...
 Mike Wynn wrote:
 isn't null equivilant to null ?

No. Taken C++: mytype a, b; mytype & A = a; mytype & B = b; // ... if (A == B) {} // here both A and B are first dereferenced, then compared. If you handle references, how are you going to compare them, when dereferencing one of them already causes an exception? To compare adress values of references, you would want to say: if (&A == &B) {} Or in D with objects, simply if (A === B) {} I simply can't see what you don't buy about it.

that was my obscure sence of humour, and also question about the semantics of D objects, which are not quite C++ references and not quite Java objects (which are a little basically unchangeable pointers with '.' replacing '->') ( null = = = null ) you agree should null.eq( null ) [null = = null] be true or throw an exception ? in java/c++ obj.equals( null ) is usually valid but null.equals( obj ) is not, why not ? should the semantics of = = be (a = = b) = = = (b = = a). as I've said b4 I don't believe a language should be driven by implementation, but driven by semantics. (and for me also robustness)
Apr 09 2003
parent "Matthew Wilson" <dmd synesis.com.au> writes:
 should null.eq(null) [null = = null] be true or throw an exception?

true, because
 language should be driven by ... semantics and ... robustness

You've got me sold.
Apr 09 2003
prev sibling next sibling parent reply "Jon Allen" <jallen minotstateu.edu> writes:
 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.
Mar 27 2003
parent reply "Matthew Wilson" <dmd synesis.com.au> writes:
"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

 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

 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

 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

 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.
Mar 27 2003
next sibling parent "Jon Allen" <jallen minotstateu.edu> writes:
Heh, sooner or later we have to agree on something.  Don't we? :-)

Anyway, here's what I think:

 Okay, I love C++ but something is entirely wrong if such an antiquated
 language manages to provide the most reliable solution to something as

 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

 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

 to manually test for identity before we can test for equality then all

 syntactic nicety is just a waste of time.

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

Basic because it's a problem that even simple programs could get bitten by. Basic, but definitely not simple.
 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

 its forebears/peers. The work Walter and I have been doing in the last

 or so on performance comparisons has shown that D does indeed kick the

 of most of its peers in a number of areas; being a C++ diehard myself,

 is a significant factor in my personnally starting to think more and more

 using D seriously.

C++ also happens to be the only member of the bunch that does initialize instances automatically. It's no accident that the equality identity problem is a non-issue in C++.
 I wonder if it wouldn't be better to have the language implicitly create

 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.]

Yeah, it does.
 I can't agree with this. Putting aside the fact that it could result in

 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

 have multiple return statements - which is not my style - or I have cannot
 return null so must use exceptions - which may not be

 What have we gained with this restriction?

I was going to say that you could use pointers to do this, but I think I found a compiler bug (or a very weird restriction, or i'm doing something very wrong) so I'll post that on a new thread.
 You could then say that it is illegal to assign null to an object

 I don't think it makes much sense to have a null object reference most


 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.


:-) Yeah, a bit empty field may not be the most brilliant solution.
 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


 declare something and don't instantiate it almost immediately?  Even


 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


 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.

 references can be null). Unless there is an efficient built-in, but
 potentially user-tailorable, handling of this then we might as well get

 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.

Point taken. I'm not proposing we scratch reference altogether, or even that we get rid of explicit "new"s. I just think it should be illegal to have a reference to nothing.
Mar 27 2003
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Matthew Wilson" <dmd synesis.com.au> wrote in message
news:b60e8s$2tg8$1 digitaldaemon.com...
 [Q: I presume that D currently initialises an uninitialised variable to
 null. Is this correct? If it doesn't, it should.]

That's correct.
Mar 27 2003
prev sibling parent "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
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

 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

 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().

 my part, though, that'd be gross. So:

 It seems that we must find a way to sensibly incorporate === in ==. We

 engineer the compiler, or mandate the class author, to incorporate

 checks in equivalence comparisons. However, there are circumstances in

 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,

 compiler provides "a bit compare of the contents of the two structs is

 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()

 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 ==

 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

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
Mar 27 2003
prev sibling next sibling parent reply "Matthew Wilson" <dmd synesis.com.au> writes:
Man, this is all too nasty. Also, I'm confused by a lot of what is being
said. Muddy stuff.


I'm not a great (language) theorist, rather I take a practical perspective.
It seems the following can be / is being said:

1. evaluating x == y with x as null (and in many cases with y also) causes
an access violation

2. syntax using == on object types and primitive types looks the same and
feels the same, yet isn't and needn't be the same

3. some people are advocating the use of contracts to guard against testing
for equivalence on null references (and other contract transgressions)

4. some are advocating a change of mindset, which will enable those of us
that think (1.) is unacceptable to get in the D (or Smalltalk) way of
thinking.


My points are

A. Practical opposition
-----------------------

You can push away on (4.) until the universe reaches entropy death, but you
are not going to convince large parts of the development community that (2.)
is true. If that is the case, why has the (partial solution) of operator
overloading in C# been done, and why has it been broadly welcomed as a big
(but still incomplete) step up from Java's == vs equals(). So whatever is
theoretically correct - assuming that's determinable - doesn't really
matter. It's what is going to work and appeal that matters.

I know this is not a democracy (nor should it be, otherwise we'll all find D
has the consistency and performance efficiency of Boost software), but I
think it would be appropriate to gather a collective opinion on this. I'll
start:

 "I am primarily, but not exclusively, a C/C++ programmer, and think that
the fact that calling == on null references results in a crash is a wart. It
will be a dissuasive factor against my using the language."

B. D is a C-family language
---------------------------

D is a C-family language. It uses { }. It uses = and ==, not, for example,
:= and =. etc. etc.

The situation, and what is being promulgated by some in this thread, is an
inconsistent indulgence.

int    i1    =    . . .
int    i2    =    . . .

if(i1 == i2)

This compares the values of i1 and i2.

SomeClass o1    =    . . .
SomeClass o2    =    . . .

if(o1 === o2)

This compares the values of o1 and o2. Of course, the "values" are the
values of the references, rather than the referenced instances. To do that
we should write

if(o1 == o2)

That's inconsistent. However, if it works, it's an inconsistency we not only
can live with, but one I think we would all want. It's the "if" that's the
issue.

There are three points of view, I think:

(a) The syntax is the same, but the semantics are different, and that is
intended and acceptable. It is the responsibility of the D
programmer/maintainer to be aware of the situation, and cater to its
requirements in their use of the language.
(b) The semantics are different, so the syntax should be different.
(c) The syntax is the same, and the semantics are the same. It is the
responsibility of the compiler to ensure that meaningful comparisons are
carried out even when the dichotomous nature of references from primitives
can lead to "no" object on one or the other side of equivalence expressions.

Obviously we are at (a). I contend that (a) is the only untenable option. If
it's different, and we should know it's different, why does it have the same
syntax? Is it a trick? Do we get to wear a badge of honour once we've been
bitten by it enough times to know - in the same way one learns to spot the
use of == rather than equals() in Java (and I was caught out on that in the
last couple of days, despite many years of caffienating) - when we see it in
the code of an unwary neophyte?

(b) would be to remove === entirely, and disallow the use of == on anything
but primitives (otherwise we'd be just where Java is). Object comparison
would have to be via EqualValue() and EqualRef() methods/globals, or
similar. This would be syntactically ugly, but would at least make sense.

Personally, I like (c). I want the inconsistency, because it makes the langu
age succinct, fits with me largely C/C++ background, and also provides the
compiler lots of opportunity for fishing out unnecessary null-tests from the
generated code. But it has to all make sense, and work in a sensible way.
I'm not saying my eq() + eqi() proposal is the way to go - I actually like
the idea of non-member operators, or at least static operators - but I am
sure that between the many large brains in this newsgroup that a robust,
elegant and efficient solution can be achieved.

C. Contracts are great, but the world is full of liars!
---------------------------------------------------

(3.) is great up to a point. D makes it even better with the built-in
unittests. It's a fab aid to development, and even more so to maintenance.
However, this is all a dressed up assert, and as such can never be valid as
the complete answer to broader system correctness. Sure it works in the
Win32 API, because it is the standard (albeit a wriggling one) on Win32. We
can rely on its crashes in a good way. (Arf arf)

But consider that one is building an application infrastructure that loads,
say, COM components that can be sourced from arbitrary providers. Is
programming to contract, employing assertions the valid approach here? Is it
bollocks. M$ tried that with ISAPI for a vanishingly short time, before
wrapping all calls to ISAPI extensions with __try __except, since the server
came crashing down with crushing regularity. Now I'm *not* saying that this
comment necessarily undercuts all the arguments on this topic about null
references (though I do believe it should give all some pause), but I see a
worrying degree of confidence throughout many of the threads on this group
that suggest that the unittest mechanism is the complete answer to
robustness. It is not.


Let's have your "statements", and comments on whether (a), (b) or (c) - with
ideas for impl (c) - is the way to go.

Matthew

P.S. It's very early here, so I will immediately resile from any offensive
statement herein with abject and grovelling apologies.



"Matthew Wilson" <dmd synesis.com.au> wrote in message
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?

 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

 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?

Mar 28 2003
next sibling parent reply Ilya Minkov <midiclub 8ung.at> writes:
Matthew Wilson wrote:
 SomeClass o1    =    . . .
 SomeClass o2    =    . . .
 
 if(o1 === o2)
 
 This compares the values of o1 and o2. Of course, the "values" are the
 values of the references, rather than the referenced instances. 

Why??? Has "another truth" become so obvious for you? More below.
 To do that we should write
 
 if(o1 == o2)
 
 That's inconsistent. However, if it works, it's an inconsistency we not only
 can live with, but one I think we would all want. It's the "if" that's the
 issue.

Wait! It is not necessarily inconsistent. You forget that in D an Object is a much more abstract concept than in C++. All of the changes made in D compared to C++ objects are targeted to be (mostly) able to think of an object just as of an "object", not as of a reference. An "object" is a user-defined type which supports Darvin's theory, and i'm not really sure it always has to be implemented as a reference. Anyway, the idea is to abstract away from references, pointers, and such. Yes, it works due to references. But don't we all think of other things when saying something different? And that's what C++ style so-called "consistency" advocates are probably especially proficient at, even to an extent that this lie replaces the truth for them. Now, you are comparing 2 rabbits. Rabbits are objects. Oh, you don't want to accidentially compare them by their properties instead of their position? I see. ;> Please don't take this post too seriously. ;) I know that a perfectly ideallistic programmer would never turn to D, as to any C descendant. :> I have done a more serious post in "identity & equivalence" thread. -i.
Mar 28 2003
parent "Matthew Wilson" <dmd synesis.com.au> writes:
 Please don't take this post too seriously. ;) I know that a perfectly
 ideallistic programmer would never turn to D, as to any C descendant. :>
 I have done a more serious post in "identity & equivalence" thread.

I can't take it seriously. Mate, I _really_ mean no offence, but I have absolutely no idea what you're talking about. Clear as custard. It may well my fault, in that I'm no great thinker on language theory and all that, but then very few are, which kind of backs up my point that D should make sense to the masses and not to the theorists.
Mar 28 2003
prev sibling next sibling parent reply "Jon Allen" <jallen minotstateu.edu> writes:
 C. Contracts are great, but the world is full of liars!
 ---------------------------------------------------

Contracts _are_ great. The world _is_ full of liars. I think there a more important reason contracts are not the solution here. In most cases all you are going to do here is manually crashing a program that would crash anyway. At the same time you are probably annoyingly moving the crash away from the line of code where the problem is. Contracts might be useful for things like ensuring a function doesn't return a null, but this isn't really the problem we are trying to address here.
 SomeClass o1    =    . . .
 SomeClass o2    =    . . .

 if(o1 === o2)

 This compares the values of o1 and o2. Of course, the "values" are the
 values of the references, rather than the referenced instances. To do that
 we should write

 if(o1 == o2)

 That's inconsistent. However, if it works, it's an inconsistency we not

 can live with, but one I think we would all want. It's the "if" that's the
 issue.

Life would definitely be easier if we weren't all such rotten creatures of habit :-)
 (a) The syntax is the same, but the semantics are different, and that is
 intended and acceptable. It is the responsibility of the D
 programmer/maintainer to be aware of the situation, and cater to its
 requirements in their use of the language.
 (b) The semantics are different, so the syntax should be different.
 (c) The syntax is the same, and the semantics are the same. It is the
 responsibility of the compiler to ensure that meaningful comparisons are
 carried out even when the dichotomous nature of references from primitives
 can lead to "no" object on one or the other side of equivalence

 Obviously we are at (a). I contend that (a) is the only untenable option.

I have to agree.
 (b) would be to remove === entirely, and disallow the use of == on

 but primitives (otherwise we'd be just where Java is). Object comparison
 would have to be via EqualValue() and EqualRef() methods/globals, or
 similar. This would be syntactically ugly, but would at least make sense.

Ugliness is a big turnoff in a language though. At least it is for me, and I suspect I'm not the only one. It's completely irrational, and I'm sure I miss a lot of neat things, but there it is.
 Personally, I like (c). I want the inconsistency, because it makes the

 age succinct, fits with me largely C/C++ background,

 But it has to all make sense, and work in a sensible way.
 I actually like the idea of non-member operators.

I'm forced to agree. It's the least evil option available :-) I have a question though, and I hope it's not to stupid:-). This entire problem is created by the idea of object as references that has become so popular lately. What is the rationale behind the object as reference idea as opposed to the C++ idea of objects as objects? To me the C++ way seems much simpler and more consistent, even with the pointers that are required for any kind of runtime object creation. It also seems like the C++ way has more potential for offloading a lot of the work that needs to be done from runtime to compile time, resulting in much better performance.
Mar 28 2003
next sibling parent reply Ilya Minkov <midiclub 8ung.at> writes:
Jon Allen wrote:
 I have a question though, and I hope it's not to stupid:-).  This entire
 problem is created by the idea of object as references that has become so
 popular lately.  What is the rationale behind the object as reference idea
 as opposed to the C++ idea of objects as objects?  To me the C++ way seems
 much simpler and more consistent, even with the pointers that are required
 for any kind of runtime object creation.

It's rather the opposite: in D objects supposed to be "just objects", while in C++ you always have underlying implementation issues. That's also the reason, why default is comparison by content in D, and not by reference. C++ way being simpler or more consistent is IMO a hallucination.
 It also seems like the C++ way has
 more potential for offloading a lot of the work that needs to be done from
 runtime to compile time, resulting in much better performance.

What makes you think that? [-Eliza] -i.
Mar 28 2003
parent reply "Jon Allen" <jallen minotstateu.edu> writes:
"Ilya Minkov" <midiclub 8ung.at> wrote in message
news:b62neg$1iai$1 digitaldaemon.com...
 Jon Allen wrote:
 I have a question though, and I hope it's not to stupid:-).  This entire
 problem is created by the idea of object as references that has become


 popular lately.  What is the rationale behind the object as reference


 as opposed to the C++ idea of objects as objects?  To me the C++ way


 much simpler and more consistent, even with the pointers that are


 for any kind of runtime object creation.

It's rather the opposite: in D objects supposed to be "just objects", while in C++ you always have underlying implementation issues. That's also the reason, why default is comparison by content in D, and not by reference.

That's patently false. Take the simplest possible case (same syntax for declaring two objects declared in both cases): MyObj a; MyObj b; Now try to work with them. Comparisons are what this is thread is about so we'll do that: if(a==b) { //do stuff } Both languages say this should compare the content of the variables. Does this work in C++? Yes. Always. No exceptions (okay it might not always _do_ what you want, but that's something D has to worry about too). No concern about the underlying implementation. I don't know, don't care whether the compiler made them references, threw them directly on the stack, or did anything else with them. Does it work in D? Maybe. To make sure that it works you have to concern yourself with the fact that a and b don't describe the actual type that I want, but that they are in fact cleverly disguised pointers to those objects. Underlying implementation becomes very important here and, to my eyes, needlessly complicates my life.
 It also seems like the C++ way has
 more potential for offloading a lot of the work that needs to be done


 runtime to compile time, resulting in much better performance.

What makes you think that? [-Eliza]

Why do we have stacks and data segments instead of just a heap? The data segment can be mapped out at compile time, leaving little to be done at runtime for statics, but D objects can't be included here because an object declaration may refer to any number of object instances, even though it probably will only ever refer to one. It's even tough (sometimes impossible) to say that it'll ever point to anything at all. It's also much cheaper to throw crap on the stack than it is to put it in the heap(not to mention easier), but putting objects on the stack in D either takes a pretty impressive compiler, or a lot of work on the user's end which will further expose implementation details and cut portability. How about the extra step the CPU has to take to dereference the link to that stuff we want to use? I realize that most classes will probably be doing this a million times anyway from the vtable alone, but I think we all want to see D kick the pants off C++ in speed at least, and every little bit helps. In C++ these things are no problem, and it's because pointers are pointers and variables are variables. As opposed to the variables are sometimes pointers, sometimes variables idea. That's not to mention the likelyhood of forgetting those "new"s at least once sometime in any decently sized project, no matter how seasoned you are. Bugs are performance problems too. I can also see it taking a lot off the hands of the GC, since it would make it much easier to free a good percentage of data. It's a lot faster to move a stack pointer or the like than it is to mark and sweep the entire heap.
Mar 28 2003
parent "Sean L. Palmer" <seanpalmer directvinternet.com> writes:
I'm all for C++'s variable allocation technique.

It truly bothers me that the only place you can make an object in D is on
the heap.  If I want a variable to be short-lived, I have to use the delete
keyword.  I have to use new also (C# is the same way).  Way too wordy for
me, for something that happens so often in a program.

Just make local object variables unable to be passed as parameters to other
functions.  If you want to pass an object to another function, return it, or
store pointers to it, you must declare an object reference instead and
allocate with new.

I guess what we want is for objects that are able to act like structs in
some situations and like classes in others?

I think let's have an explicit reference keyword also.  Say I have a rather
large struct I want to pass around as an 'in' parameter.  I don't want it to
be inout.  But I do want it to be by reference because it's large.  I would
like to declare:

bool TestMyBigObject(in ref MyBigObject obj);

There's no hard line between structs and classes.  It's quite blurry.

Sean

"Jon Allen" <jallen minotstateu.edu> wrote in message
news:b62s2d$1ltc$1 digitaldaemon.com...
 "Ilya Minkov" <midiclub 8ung.at> wrote in message
 news:b62neg$1iai$1 digitaldaemon.com...
 Jon Allen wrote:
 I have a question though, and I hope it's not to stupid:-).  This



 problem is created by the idea of object as references that has become


 popular lately.  What is the rationale behind the object as reference


 as opposed to the C++ idea of objects as objects?  To me the C++ way


 much simpler and more consistent, even with the pointers that are


 for any kind of runtime object creation.

It's rather the opposite: in D objects supposed to be "just objects", while in C++ you always have underlying implementation issues. That's also the reason, why default is comparison by content in D, and not by reference.

That's patently false. Take the simplest possible case (same syntax for declaring two objects declared in both cases): MyObj a; MyObj b; Now try to work with them. Comparisons are what this is thread is about

 we'll do that:

 if(a==b)
 {
     //do stuff
 }

 Both languages say this should compare the content of the variables.

 Does this work in C++? Yes.  Always.  No exceptions (okay it might not
 always _do_ what you want, but that's something D has to worry about too).
 No concern about the underlying implementation.  I don't know, don't care
 whether the compiler made them references, threw them directly on the

 or did anything else with them.
 Does it work in D?  Maybe.  To make sure that it works you have to concern
 yourself with the fact that a and b don't describe the actual type that I
 want, but that they are in fact cleverly disguised pointers to those
 objects.  Underlying implementation becomes very important here and, to my
 eyes, needlessly complicates my life.

 It also seems like the C++ way has
 more potential for offloading a lot of the work that needs to be done


 runtime to compile time, resulting in much better performance.

What makes you think that? [-Eliza]

Why do we have stacks and data segments instead of just a heap? The data segment can be mapped out at compile time, leaving little to be done at runtime for statics, but D objects can't be included here because an

 declaration may refer to any number of object instances, even though it
 probably will only ever refer to one.  It's even tough (sometimes
 impossible) to say that it'll ever point to anything at all.

 It's also much cheaper to throw crap on the stack than it is to put it in
 the heap(not to mention easier), but putting objects on the stack in D
 either takes a pretty impressive compiler, or a lot of work on the user's
 end which will further expose implementation details and cut portability.

 How about the extra step the CPU has to take to dereference the link to

 stuff we want to use?  I realize that most classes will probably be doing
 this a million times anyway from the vtable alone, but I think we all want
 to see D kick the pants off C++ in speed at least, and every little bit
 helps.

 In C++ these things are no problem, and it's because pointers are pointers
 and variables are variables.  As opposed to the variables are sometimes
 pointers, sometimes variables idea.  That's not to mention the likelyhood

 forgetting those "new"s at least once sometime in any decently sized
 project, no matter how seasoned you are.  Bugs are performance problems

 I can also see it taking a lot off the hands of the GC, since it would

 it much easier to free a good percentage of data.  It's a lot faster to

 a stack pointer or the like than it is to mark and sweep the entire heap.

Mar 29 2003
prev sibling parent reply "Matthew Wilson" <dmd synesis.com.au> writes:
 Ugliness is a big turnoff in a language though.  At least it is for me,

 I suspect I'm not the only one.  It's completely irrational, and I'm sure

 miss a lot of neat things, but there it is.

For me also. However, given the choice between ugliness and prettiness_with_inconsistency, then it has to be ugliness. (I keep calling it inconsistency, since the collegial nature of the Digital Mars newsgroups and my own good manners prevents me from using more vivid language.) To digress for a moment, I am actually intrigued as to why this issue has me so hot under the collar. It's been precipitated by my being nearly all the way through a different article I'm writing about how to efficiently override C#'s Equals() method, in which I observe the nonsensical ways in which both Java and, to a lesser extent, C# handle the identity/equivalent issue, and I had written that D had the issue solved. That prompted the earlier post which then led Farmer to give me the news of the grim reality that we've been debating since. I don't think this can explain it all, however. I think that, having been away from the D newsgroup for some months, now that I've dived back into D (and will be having more to do with it for a few different writing projects in the foreseeable future) it seems to be much closer to a _real_ language. Last year I thought it was kind of interesting in an intellectual way, but now I can actually imagine doing significant amounts of my work in D and, though I'll be shot by my C++ friends, even preferring D to C++ for some non-trivial tasks.
 I'm forced to agree.  It's the least evil option available :-)

 I have a question though, and I hope it's not to stupid:-).  This entire
 problem is created by the idea of object as references that has become so
 popular lately.  What is the rationale behind the object as reference idea
 as opposed to the C++ idea of objects as objects?  To me the C++ way seems
 much simpler and more consistent, even with the pointers that are required
 for any kind of runtime object creation.  It also seems like the C++ way

 more potential for offloading a lot of the work that needs to be done from
 runtime to compile time, resulting in much better performance.

The answers to that one are endless, I fear. I think some of it has to do with basing D on GC, which is arguably a good thing, as all the objects then have to be heap-based. This means references, or pointers, or whatever name you want to give the concept, but it means that the thing can be null. C++ is arguably more flexible, in providing for both types. (And this is recognised by C# in that they place what they call value types on the stack, even though they still new() them. Bit Yucky.) But C++ also has lots of problems as a result of that flexibility. The fact is, there is no perfect world in S/E, just as in anything else. I am increasingly persuaded that D is a very good local maxima. It may well soon become the most "sensible" language, at least that I'm aware of. But it will never be the perfect language, because such a thing is impossible. There are valid, and widely used, programming paradigms/facilities that are simply incongruent, so all languages must make a compromise in their support for them. (Of course, there are facilities that are omitted by mistake, nothing else. Java has more of these than any language I can think of - anyone think of a reason why Java cannot have const, for example? - but all suffer from them. C++ doesn't allow local functions. C# and Java don't support RAII, and we may justly scoff at them for it. C has all that struct/enum namespace guff.) Thus, the issue we're talking about is a good example of the great language debate. == in D is a fudge, a con, a trick of the compiler's light, but it is one that we want, since we want to be able to use == on the values of references (for convenience), and we will likely want it for genericity within templates (for power). Assuming everyone can admit that, we can further admit that neither side is correct in an absolute sense. Given that, we have to consider whether D's == operator serves the aims of D, and its likely success: 1. Is it likely to lead to more or fewer errors in its current guise? 2. Does it serve to encourage/discourage the use of D with C/C++ users? With Java users? C#? Smalltalk? Delphi? etc. 3. Would changing it unnecessarily complicate the compiler? 4. Can it be altered to include identity in an efficient way? These questions, apart from (3.), cannot be answered by a single poster to the group. It's something we should all opine on, such that a decision can be made. I was thinking about this last night, and wondering whether it really was important enough to have stimulated all this debate (a lot of it from me, I grant you). I was thinking through the languages I know, and what they did. C doesn't have the issue, because things are either pointers or they are values. C++ doesn't have it because references cannot be null (unless you're the victim of some perverse pointer dereferencing trickery...) Java is the signal example of this issue, and takes the position that equals() must be applied to all object instance references, and == to all primitives. This confounds the inexperienced a lot, and still the experienced occasionally. I think C# is where we need to look for the answer. They've left out, or messed up, a great many concepts that C/C++ programmers hold dear, presumably due to the pressures of keeping the language simple and of getting it all developed in a reasonable time. However, they've taken the effort to address the Java inconsistency, in providing overloading for ==. We have to ask why? It's not just syntactic sugar, to win over C++ diehards, because it's still nearly as painfully verbose as Java in other aspects. Can anyone suggest a reason beyond that they don't want to suffer from Java's identity problem (arf, arf)? Further evidence that that is their intent is the fact that operators == and != are declared as static members of the class. This allows comparison with instances on one or the other or both sides of an expression, and it also protects against x == y with x (or y) as null. The advised implementation of == is public static bool operator ==(MyClass x1, MyClass x2) { return Object.Equals(x1, x2); } Object's static Equals(o1, o2) tests x1 and x2 for null, before calling the instance Equals() on one of the operands. So again, why have the promulgators of VB, ASP, Fox, MFC, and all the other dumbed down and offensively simplistic guff spent effort on effecting this sophistication in C#? It's not the last bit of polish of their otherwise perfect language. It's not to win over C++ programmers, who have many other areas on which to wince. I can only surmise that they deemed it sufficiently important, and I suggest that we should do so for D. Yours enthusiastically Matthew
Mar 28 2003
parent reply "Luna Kid" <lunakid neuropolis.org> writes:
Matthew, I followed through this lively thread (not 100%
thouroughly though, so I may make a big mistake by this quick
message now...), and I'm still unsure about two points.
(So I'm not arguing, just asking below, to see more clearly.)

1.)

 You seem to say you don't want to check for "being
 not null" when doing ==, but want D doing that for
 you implicitly. Correct?

 But would you want the compiler to do if, indeed,
 a == 0?

 Do you consider 0 valid or invalid in this generic
 context?

 If 0 is valid, then == should just return true for
 a == b, if both are null. Is that what you propose?
 (I have not thought about it being good or bad.)

 Or, an exception should be thrown, if you consider
 0 invalid. But in that case, of course, as you don't
 want to have a check in the code, we have not
 progressed from the access violation case.

2.)

 You seem to say that a == x in C++ is safe, because
 there is no access violation that way.

 Well, in fact, it *is* possible (as you said, though,
 by "some perverse pointer dereferencing trickery" --
 which I *have* encountered), and a C++ program will
 then crash the exact same way a D program would, if
 an object reference is 0.

 As I don't know enough about D pointers and references
 yet, it can only make sense to me if D lets you assign
 0 to a reference more "conveniently" than C++, possibly
 to the extent that D considers 0 references legal,
 while C++ considers them illegal.

 Do I understand correctly that that is the source of
 all this pain here?


Anyhow, it all boils down to this:

If D considers 0 being legal reference values (supporing
assigning 0, for instance), then it should indeed check
them as said in (1.) above, and not raising any error.

If D considers 0 references illegal, then it should blow
up programs loudly (as also said above), but then it also
must

    - clearly state that 0 references are illegal

    - prevent creating dangling object references
      as much as possible

(Walter?)

Thanks for your patience,
Luna Kid
Mar 29 2003
parent "Luna Kid" <lunakid neuropolis.org> writes:
     - prevent creating dangling object references

Sorry, I meant: prevent creating zero references, most importantly.
Mar 29 2003
prev sibling next sibling parent Antti Sykari <jsykari gamma.hut.fi> writes:
"Jon Allen" <jallen minotstateu.edu> writes:

 C. Contracts are great, but the world is full of liars!
 ---------------------------------------------------

Contracts _are_ great. The world _is_ full of liars. I think there a more important reason contracts are not the solution here. In most cases all you are going to do here is manually crashing a program that would crash anyway. At the same time you are probably annoyingly moving the crash away from the line of code where the problem is.

My impression is that contracts bring the crash to precisely where the problem is. Had the contract not been verified, the program might crash soon (if you're lucky), unexpectedly after a long time (causing a fun weekend of bug-hunting), or not at all (even worse). -Antti
Mar 29 2003
prev sibling next sibling parent reply Antti Sykari <jsykari gamma.hut.fi> writes:
I found the discussion about references confusing, so here's a short
summary of how different languages handle equality. I include Java,
C++, C# and Eiffel.  Eiffel, because it also includes the notion of
"deep equality", which shows that the value/reference equality thing
is not everything.  Eiffel also seems to have a lot of careful thought
behind its design.

Leaving aside function pointers, delegates, strings, arrays and other
borderline cases, there are two kinds of variables:

- those with value semantics (D: basic types and structs, objects
  in C++ when treated as objects instead of pointers to objects. Primitive
  types in Java. Value types in C#. Expanded types in Eiffel.)
- those with reference semantics (objects in C#, Java, D, Eiffel).

Then there a couple of useful and often-used equality comparison
methods. (And infinitely many others - you can always roll our own if
you need a customized equivalence relation between objects and/or
values.) Common ones that I can think of are:

1) reference equality: does X refer to the same object as Y?
 - "===" in D, "==" in Java/C# and C++ (if object is a pointer) "=" in Eiffel
 - if a reference type, compare the pointers
 - if a value, then use method 2 (logically, this is nonsense, since
   two different integers never refer to the same integer unless they
   really are the same integer - but so the common thinking seems to go)

2) value equality: does X have the same value as Y?
 - if it's a value, bitwise (or user-defined) comparison
    - "==" in D, Java/C#/C++, "=" in Eiffel
 - if it's a reference type:
   - "==" in C++ (if object is a reference or the thing itself)
   - "==" in D, "X.equals(Y)" in Java, "X.Equals(Y)" in C# but in C#
     you should overload operator==(X, Y) to do this
   - in D that's just a call of Object.eq(other) which does
     whatever the implementation decides; in current Phobos, it's just
     this === other)
   - IMO, should be something like:
     - compare its reference members with reference equality (method 1)
     - compare its value members with value equality (method 2)
   - which is precisely what it does in Eiffel ("equal" operator)

3) recursive value equality: does X have the deep structure as Y?
 - not seen in many languages; compares the two objects recursively
 - "deep_equal" in Eiffel
 - if it's a value, bitwise comparison
 - if it's a reference type:
   - compare its reference members recursively (method 3)
   - compare its value members with value equality (method 2)

It's interesting to note that you don't need deep equality very often.
Maybe that's why many popular languages (including lack the notion in
the first place. (Although deep clone might be useful -- for example,
http://www.javaworld.com/javaworld/javatips/jw-javatip76.html. By
serializing, you get also deep equality for free.)

Eiffel has methods clone and deep_clone which have as postconditions,
respectively, that result.equal(original) and result.deep_equal(original).

Despite what has been said here earlier, D (as it currently stands)
seems to be the most consistent language: "==" is always "value
equality" and "===" always "reference equality".  Still, I don't know
if that's a good or not, taking into consideration the popularity of
languages that seem to think differently: C++, Java and C#.  There are
undoubtetly reasons behind that...

I have some more thoughts on this, but they will need some time to
settle down, so they will be left to another posting.

-Antti
Mar 29 2003
parent reply "Luna Kid" <lunakid neuropolis.org> writes:
 Let's see... actually, there seems to be internal inconsistency in the
 D documentation. expression.html says that

 Object x = null, y = null;
 x == y;

 is legal and true (null == null is true), but operatoroverloading.html
 tells us that == will be rewritten as x.equals(y), which will cause a
 null pointer exception if x is null.  Which is even worse.

 Wonder which one it is.

Oops, I just tried it: Object o = null; Object p = null; if (o == p) { ... } This is legal, but still breaks! So, Matt, now I also understand your point much better -- and unrerstand Walter's point much less... Luna Kid
Mar 29 2003
parent reply "Matthew Wilson" <dmd synesis.com.au> writes:
Being quite thick in the head when it comes to reading - classic
picture-thinker problems - I'm quite confused by your use of 0, a numeric,
where I suspect you're actually meaning null. I'm answering with the
assumption that you mean null, this predicated on a further assumption
(shaky ground, assumptions on assumptions) that you're a good C++ chap and
eschew NULL in favour of 0.

That being my assumption, answers inline

----- Original Message -----
From: "Luna Kid" <lunakid neuropolis.org>
Newsgroups: D
Sent: Sunday, March 30, 2003 8:59 AM
Subject: Re: null == o?


 Matthew, I followed through this lively thread (not 100%
 thouroughly though, so I may make a big mistake by this quick
 message now...), and I'm still unsure about two points.
 (So I'm not arguing, just asking below, to see more clearly.)

 1.)

  You seem to say you don't want to check for "being
  not null" when doing ==, but want D doing that for
  you implicitly. Correct?

Yes
  But would you want the compiler to do if, indeed,
  a == 0?

in a comparison x == y x is null; y is null - result: true (result deduced by the compiler-inserted check) x is null; y is non-null - result: false (result deduced by the compiler-inserted check) x is non-null; y is null - result: false (result deduced by the compiler-inserted check, so any equals() method can be sure of the rhs parameter being non-null; makes things nice and simple) x is non-null; y is non-null - result is result of x.equals(y) (deduced is same way) In effect, and this is kind of inspired with Antti's comments (though I don't think I agree with much of what he said), I am suggesting that == is translated into a call to static bit __Reference.equals(Object o1, Object o2) // __Reference is the built-in, inaccessible, type of which instances hold "pointers" to instances of programmer-accessible types { return x === null ? (y === null ? true : false) : (y === null ? false : x.equals(y)); }
  Do you consider 0 valid or invalid in this generic
  context?

null has to be valid, as there are simply too many times that one would wish to use a null value of a reference to indicate some special condition (whether that be failure of something, or uniitialised state of something, ad infinitum). This is the source of the problem. We want to "hide" the notion of pointers behind references, but we still want to be able to have null ones. If we accept this contradiction for the sake of utility (which I believe we should), then we have the issue of what == means for reference types. == is identity ------------- Some would argue that, a la Java, the value of a reference is its "pointer", which does make some sense. (All points of view on this issue make some sense. That's the vexing part of it.). The problem in this case is that though the semantics are consistent, the syntax is not (or, rather, the syntax gives the reader an impression that is incorrect.) If I write x == y then this compares by value for value types, and by identity for reference types. Some say consistency, some say inconsistency. Both are right, neither wrong. Perspective. Nasty. Where the weight falls down on my side - but of course he'd say that!, you cry - is when one considers the use of templates. If I write a template that, say, searches an unordered sequence to elide duplicate (by value), then I am stymied. If I write it for value types it will work on identity for reference types. Not what I want. If I write if for reference types (using equals()) then it won't work for value types (my assumption is that calling .equals() on an int is an error ...) Why should I have to specialise this simple algorithm. Sucks bad. Of course, Java can survive this because it doesn't support templates. (It'll be interesting to see how it handles that when/if Sun every get round to making good their promise to include them.) == is value ----------- This is the current situation with D. For general convenience, appropriate terseness, and templates I agree with it. However, the problem is that == doesn't take account of whether its operands are null, resulting in a crash. Consider an example I've just been working on. I've written classes/funcs in a number of languages to tokenise a string based on char and/or string delimiters, and to elide or preserve blanks in respect of parameter values. The returned value is an array of char arrays. If blanks are preserved, then the entries for such are empty strings, in other words char arrays of zero length. Now it's not too hard to imagine the same components returning instantiated objects, which have been serialised in a text format, for, say, passing an array of objects between hosts. A blank token ". . .xhfjdk][][sadjd2339 . . ." would result in a null object being returned in the array of polymorphic instances. But now I want to apply an algorithm to the array. I pass the array as one argument, and a sentinel object to the other. (Imagine the D equivalent of STL's find_if). The algorithm uses ==. The program crashes on the null array entries. I have to write a separate algorithm. I am unhappy and disatisfied. More code, bigger exe, more maintenance, less reuse, blah, blah
  If 0 is valid, then == should just return true for
  a == b, if both are null. Is that what you propose?
  (I have not thought about it being good or bad.)

Yes, see above
  Or, an exception should be thrown, if you consider
  0 invalid. But in that case, of course, as you don't
  want to have a check in the code, we have not
  progressed from the access violation case.

 2.)

  You seem to say that a == x in C++ is safe, because
  there is no access violation that way.

  Well, in fact, it *is* possible (as you said, though,
  by "some perverse pointer dereferencing trickery" --
  which I *have* encountered), and a C++ program will
  then crash the exact same way a D program would, if
  an object reference is 0.

I don't think this is relevant to the debate. Any language that respects its practitioners as being professional and competent is inevitably going to leave holes that can be exploited by the, though informed, perverse individual.
  As I don't know enough about D pointers and references
  yet, it can only make sense to me if D lets you assign
  0 to a reference more "conveniently" than C++, possibly
  to the extent that D considers 0 references legal,
  while C++ considers them illegal.

  Do I understand correctly that that is the source of
  all this pain here?

Pretty much, yes. We can happily pretend that pointers are references if references can never be null. But is it worth the strictures that this would impose?
 Anyhow, it all boils down to this:

 If D considers 0 being legal reference values (supporing
 assigning 0, for instance), then it should indeed check
 them as said in (1.) above, and not raising any error.

Glad you agree. :)
 If D considers 0 references illegal, then it should blow
 up programs loudly (as also said above), but then it also
 must

     - clearly state that 0 references are illegal

     - prevent creating dangling object references
       as much as possible

 (Walter?)

 Thanks for your patience,
 Luna Kid

Mar 29 2003
parent "Luna Kid" <lunakid neuropolis.org> writes:
 ... I suspect you're actually meaning null. I'm answering with the
 assumption that you mean null, this predicated on a further assumption
 (shaky ground, assumptions on assumptions) that you're a good C++ chap and
 eschew NULL in favour of 0.

Sorry about driving you into that... But anyhow, you guessed both things right. :) And thanks for the confirming _my_ assumptions (regarding this == business). Cheers, Luna Kid [need to find a better name than Szabolcs Szasz...]
Mar 29 2003
prev sibling next sibling parent Antti Sykari <jsykari gamma.hut.fi> writes:
IMO, "==" should mean value equality for value types and reference
equality for reference types.

This is not very religious issue for me, but there are some reasons as
to why I would prefer the situation to be that way.


Reason 1. The thing that is done most often should look the simplest.

That's because the most common operation for value types is value
comparison and the most common operation for reference types is
reference comparison.  Then we follow the rule "the most common and
simple things should look and feel simple."

What is now done for "==" is not common (at least I find that I rarely
use value comparisons in C++ objects, and where I do, they are
struct-like objects which should not be heap-allocated in the first
place) nor simple. I take it that currently it's translated for
something like this:

bit operator==(object a, object b)
{
  return (a === null && b === null)
    || (a !== null && b !== null && (a.eq(b) || b.eq(a)));
}

At least if you've to believe what is said in
http://www.digitalmars.com/d/expression.html.

Let's see... actually, there seems to be internal inconsistency in the
D documentation. expression.html says that 

Object x = null, y = null;
x == y;

is legal and true (null == null is true), but operatoroverloading.html
tells us that == will be rewritten as x.equals(y), which will cause a
null pointer exception if x is null.  Which is even worse.

Wonder which one it is.  Might even go as far as to install the
compiler myself and test, now that I even have Windows on my computer.

At any order, I'd still very much prefer "===" or "equals" or
something like that to mean the more complicated case ("return true if
both null or both non-null and a.equals(b) is true, and otherwise
false") and "==" to mean the simpler case (compare identity).


Reason 2. Consistency with the operator "=".

"=" and "==" are closely related.  "=" copies the value or reference
of a variable to another.  "==" inspects, or should inspect, if two
values or two references are the same.

It's instructive to look at the following code snippet and think about
its meaning (assume, for example, that you're in one of them dreaded
maintenance programming positions)

{
  <Type> a, b;

  //... a lot of code here so you don't remember what the types of a and
  //    b are ...

  a = b;  // what does this mean?
}

Now, what happens during the assignment?  Well, it depends on if
<Type> is a reference or value type!  If the language was stubbornly
consistent, it would demand that "a = b" would call b.clone() if the
type in question were a reference one, and otherwise do a simple
bitwise copy.

My point is that because "a = b" does not call clone() on b, "a == b"
should not call equals() on a.


And, to take the question further...

Reason 3.  Generic containers.

Here I'd rather attempt to grow more understanding and promote
discussion than to blindly try to push my opinion (as was the case
earlier ;-), since the generics in D are still quite unknown terrain.

Suppose that we have, for example, a Set template that can
contain both basic types and objects, and that we've just instantiated
Set(int) as intSet and Set(object) as objectSet.

f() {
  intSet intset = new intSet();
  objectSet objectset = new objectSet();

  intset.insert(5);
  intset.insert(5); // ok, overwrites the old integer
  objectset.insert(new XWindowSystem(":0.0"));
  objectset.insert(new TCPIPNetwork());
  objectset.insert(new MeaningOfLife(42));
  objectset.insert(new MeaningOfLife(42)); // ok, won't overwrite
                                           // ... or does it?
  objectset.insert(null); // surely we can have null objects in
                          // containers, right?
}

Do we want the objectset to use reference comparison or value
comparison internally?  (Or do we want to get two or just one
different MeaningOfLife objects when later iterating the set? Depends
on the application, I'd guess...)

In C++ we would just use set<int> or set<Object*> and, hence, be
explicit about our choice.  But in D, there is not much room for being
explicit because pointers are hidden inside object references.

Anyway, let's say that I'd want reference comparison semantics if
possible. We have then two alternatives:

- specialize the template for things of type Object and use ===
  there instead -- which would probably lead to duplication of code
  in every container which wants to store (or find, or replace, or
  whatever you do with objects inside containers!) objects
  identity-wise

- attack the "root" of the problem, and make == mean reference
  equality. Which, of course, greatly simplifies all template code.
  The downside being that we have to specialize templates for Objects
  if we suddenly want value semantics.  Can't get everything, can you?

Now, that might not have been the best of examples... 


Conclusion:

The thing about objects, is not as much about their values as it is
about their identity, so == should mean reference equality.

Identity is the essence of a reference type; value is the essence of a
value type.

The essence is what should be compared against when the programmer
says "==" -- regardless of whether he knows the type he's going to
use, as in normal code, or doesn't, as when he's writing template
code.

(Oh yeah, and Eiffel, Java and C# are pretty much on the same road,
too.)

-Antti
Mar 29 2003
prev sibling next sibling parent Antti Sykari <jsykari gamma.hut.fi> writes:
While I was writing the previous article, it occurred to me that
different concepts of equivalence could be implemented if it were
possible to overload operator == at the module scope. It would be
quite useful.

The overloaded operators then become part of the module's interface,
as in C++.

I also promoted consistency with the assignment operator, so that if
== means value equivalence, then = should mean cloning, and if ==
means reference equivalence, then = should mean copying reference, and
so on. This scheme would made that possible as well.

As examples, I'll provide hypothetical classes "BackAccount" and "String".

module backaccount;

// operator = that just copies the reference.
// this would be the one that's generated by default.
void assign(inout BankAccount lhs, BankAccount rhs)
{
  lhs = rhs; // let's pretend that lhs = rhs here means assigning the
             // reference for real, and outside the module it means
             // calling backaccount.assign(lhs, rhs);
}

// operator == that compares the identities of the objects.
// again, this would be the default-generated one.
bit eq(BankAccount lhs, BankAccount rhs)
{
  return lhs == rhs; // the same caution as above
}

// Standard bank account example
class BackAccount
{
  this(int initialAmount) { ... }
  void withdraw(int amount) { ... }
  void deposit(int amount) { ... }
  // ...
private:
  // internal state  
}

And there you go, a bank account with all the way reference
semantics...


module string;

// operator = which clones the string (a real world example might do
// reference counting and copy-on-write or something like that)
void assign(inout String lhs, String rhs)
{
  lhs = rhs.clone();
}

// operator == which compares the values of the string
void eq(String lhs, String rhs)
{
  return lhs.m_contents == rhs.m_contents;
}

class String
{
  this(const char*) { ... }
  String clone() { ... }
  char at(int index) { return chars[index]; }

private:
  char[] m_contents; 
  // ptr 
}

And there you go, a value-oriented class.

-Antti
Mar 29 2003
prev sibling parent Antti Sykari <jsykari gamma.hut.fi> writes:
Not enough replying to myself today, it seems :)

Antti Sykari <jsykari gamma.hut.fi> writes:
 Reason 3.  Generic containers.

 Here I'd rather attempt to grow more understanding and promote
 discussion than to blindly try to push my opinion (as was the case
 earlier ;-), since the generics in D are still quite unknown terrain.

 Suppose that we have, for example, a Set template that can
 contain both basic types and objects, and that we've just instantiated
 Set(int) as intSet and Set(object) as objectSet.

 f() {
   intSet intset = new intSet();
   objectSet objectset = new objectSet();

   intset.insert(5);
   intset.insert(5); // ok, overwrites the old integer
   objectset.insert(new XWindowSystem(":0.0"));
   objectset.insert(new TCPIPNetwork());
   objectset.insert(new MeaningOfLife(42));
   objectset.insert(new MeaningOfLife(42)); // ok, won't overwrite
                                            // ... or does it?
   objectset.insert(null); // surely we can have null objects in
                           // containers, right?
 }

Here we might also get "an impossible situation" easily. Let's suppose, for the example, that == is value equivalence and === is reference equivalence. A set of objects is supposed to maintain the invariant that an object is in the set only once, or that there are no objects x and y so that set.has(x) && set.has(y) && x == y && x !== y. As we all know from mathematics. Or, to put that into pseudocode: foreach (x in set.objects, y in set.objects) { if (x !== y) assert(x != y); } But now it could happen that we have... class Integer { this(int i) { m_i = i; } int i; } Object first = new Foo(1), second = new Foo(2); set.insert(first); set.insert(second); // ok; invariant maintained; first != second second.i = 1; // this will break the set invariant; first == second ! // there are suddenly two objects inside the set that // have the same value (but still different identity) And, so, I am forced to answer my own question:
 Do we want the objectset to use reference comparison or value
 comparison internally?

Yes. (Otherwise, integrity might be violated.) Of course, that isn't a necessity for all containers. -Antti
Mar 29 2003
prev sibling parent reply Karl Bochert <kbochert copper.net> writes:
On Wed, 26 Mar 2003 15:17:36 +1100, "Matthew Wilson" <dmd synesis.com.au> wrote:

about Zortech C) but I think he's !dead! wrong on this one. int i; MyObj o; 1) i is not a value. i is a symbol that refers to some storage. When I type i = 5; I am not storing 5 into i, but into the storage that i refers to. One of the glories of the compiler is that it allows me to ignore this distinction. Why should not it do the same for o? 2) It makes sense to have a pointer that doesn't point to anything, but a reference that doesn't refer to anything seems illogical. When I type 'MyObj o', am i not creating a reference to something; Why did I type it then? 3) if r1 and r2 are references, then is 'if (r1==r2)' the same thing as 'if (*r1 == *r2)' where r1 and r2 are pointers? What then is the point? Save two asterisks? 4) 'int i' declares a bit of storage for an int? why does 'MyObject o' just create a symbol table entry? Are not 'int' and 'MyObj' both datatypes? The difference here seems entirely gratuitous. 5) Forcing an exception for uninitialized objects makes as much sense as forcing an exception for an uninitialized int. I see 2 ways to deal with this: a) 'MyObj o' creates an actual instance of MyObj. The language could allow access to this prototype object or not. b) 'MyObj o' creates a reference to a null object. o is immediately a reference to an object (like it claims to be!) but is unlikely to compare with an initialized object. Karl Bochert
Mar 28 2003
parent reply Derek Parnell <Derek.Parnell No.Spam> writes:
On Fri, 28 Mar 2003 23:09:40 GMT, Karl Bochert <kbochert copper.net> wrote:

 On Wed, 26 Mar 2003 15:17:36 +1100, "Matthew Wilson" <dmd synesis.com.au> 
 wrote:

some questions about Zortech C) but I think he's !dead! wrong on this one. int i; MyObj o; 1) i is not a value. i is a symbol that refers to some storage. When I type i = 5; I am not storing 5 into i, but into the storage that i refers to. One of the glories of the compiler is that it allows me to ignore this distinction. Why should not it do the same for o?

I have a similar point of view to Karl on this. Conceptually, I regard both 'i' and 'o' in this example to represent run-time entities. That is, something that exists in addressable memory for a period of time. And that all entities have certain properties ( or attributes if you like), such as "CanBeWrittenTo", "Initialized", "CanBeUpdated", "Value", "Identity", "MemoryAddress", "LogicalSize", "PhysicalSize", "DataType", "ValueDataType", and the list goes on... I know that most of these properties cannot be directly accessed by D coders, but I'm talking in conceptual terms here. Amongst the operations that coders will need to perform with entities, is to check if two entities have the same value, and to check if two entities are actually the same entity or not. To me, consistency would argue that "==" is the operation symbol for checking the value of the entities, and "===" is the operation symbol for checking the identity of the entities. Such that "if o1 == o2" is like shorthand for "if o1.value is the same as o2.value" and "if o1 === o2" is like shorthand for "if o1.identity is the same as o2.identity". As for the expression "if (o1)", to me this is like shorthand for "if o1.initialized is true". -- Derek
Mar 30 2003
parent reply "Matthew Wilson" <dmd synesis.com.au> writes:
Derek

Good points. However, you've not addressed the core issue of this current
debate, which is whether == should crash if one or both operands are null
(for reference types, of course), or whether it should do a "safe"
comparison. This is the issue we cannot find agreement on.

Matthew

"Derek Parnell" <Derek.Parnell No.Spam> wrote in message
news:oprmvkc1jryj5swd news.digitalmars.com...
 On Fri, 28 Mar 2003 23:09:40 GMT, Karl Bochert <kbochert copper.net>

 On Wed, 26 Mar 2003 15:17:36 +1100, "Matthew Wilson"


 wrote:

some questions about Zortech C) but I think he's !dead! wrong on this one. int i; MyObj o; 1) i is not a value. i is a symbol that refers to some storage. When I type i = 5; I am not storing 5 into i, but into the storage that i refers to. One of the glories of the compiler is that it allows me to ignore this distinction. Why should not it do the same for o?

I have a similar point of view to Karl on this. Conceptually, I regard

 'i' and 'o' in this example to represent run-time entities. That is,
 something that exists in addressable memory for a period of time. And that
 all entities have certain properties ( or attributes if you like), such as
 "CanBeWrittenTo", "Initialized", "CanBeUpdated", "Value", "Identity",
 "MemoryAddress", "LogicalSize", "PhysicalSize", "DataType",
 "ValueDataType", and the list goes on...

 I know that most of these properties cannot be directly accessed by D
 coders, but I'm talking in conceptual terms here.

 Amongst the operations that coders will need to perform with entities, is
 to check if two entities have the same value, and to check if two entities
 are actually the same entity or not.

 To me, consistency would argue that "==" is the operation symbol for
 checking the value of the entities, and "===" is the operation symbol for
 checking the identity of the entities.

 Such that "if o1 == o2" is like shorthand for "if o1.value is the same as
 o2.value" and "if o1 === o2" is like shorthand for "if o1.identity is the
 same as o2.identity".


 As for the expression "if (o1)", to me this is like shorthand for "if
 o1.initialized is true".


 --
 Derek

Mar 30 2003
next sibling parent Derek Parnell <Derek.Parnell No.Spam> writes:
On Mon, 31 Mar 2003 11:45:25 +1000, Matthew Wilson <dmd synesis.com.au> 
wrote:

 Derek

 Good points. However, you've not addressed the core issue of this current
 debate, which is whether == should crash if one or both operands are null
 (for reference types, of course), or whether it should do a "safe"
 comparison. This is the issue we cannot find agreement on.

 Matthew

Yeah, I know ;-) I bow out of that debate for now. My mind is not so clear about which way to bend yet. My current thinking (subject to massive fluctuation), depends on what is meant by "null". I think what people are trying to say is that it is possible for an entity (reference) to be unusable sometimes. If this is the case, and that we want coders to be micro-managing their code, then any attempt at using that entity should result in an exception. By "using" I mean any run-time access to any of the entity's properties. To aid the coder in micro-managing their code, it would be nice to be able to avoid exceptions by explicitly testing if an entity is usable or not. if (available(o1) && available(o2)) { if (o1 == o2 ) { . . . As to whether this availablity checking should be implicit or not seems to be where the debate is mainly stuck. If we assume 'implicit' is a possiblity (that is, the compiler is allowed to do some micro-managing) then if either entity is not available, any relationship or identity testing should always return False. My reasoning is that if 'o1' is not available then how can its non-existant value be the same as o2.value? And if 'o1' is not available then it's non-existant identity can never be the same as o2.identity. My own opinion for now, is that I would like to see the language have both implicit 'null'/availabilty testing and the ability for coders to do explicit 'null'/availability testing. By the way, this would extend to having some form of syntax that allowed the coder to explictly make an entity unavailable. -- Derek
Mar 30 2003
prev sibling parent reply Helmut Leitner <helmut.leitner chello.at> writes:
Matthew Wilson wrote:
 
 Derek
 
 Good points. However, you've not addressed the core issue of this current
 debate, which is whether == should crash if one or both operands are null
 (for reference types, of course), or whether it should do a "safe"
 comparison. This is the issue we cannot find agreement on.

Then I'll add my three reasons why it shouldn't crash. (1) It is against the expectations of programmers. If you do OO programming in C, C++ or Java, you will write exactly that (and expect it to be save) (2) It is against the very idea of operator overloading, which must have transparency to achieve its goal. That is, you must be able to forget that the operator may be a method of an object. i==0 cant crash, so null==object shouldn't be able to crash too. (3) Any crash is bad. If there is a sensible way to continue operation in a situation we should take the chance to tolerate it. A "close" on a file that is not open - fine, lets go on. A free on some pointer that is NULL - whats the problem? An object that doesn't exist compared to null in D - well sure it should return "true". Nothing else would make more sense. -- Helmut Leitner leitner hls.via.at Graz, Austria www.hls-software.com
Mar 30 2003
next sibling parent "Luna Kid" <lunakid neuropolis.org> writes:
Helmut Leitner wrote:
 Then I'll add my three reasons why it shouldn't crash.

 (1) It is against the expectations of programmers. If you do OO

     in C, C++ or Java, you will write exactly that (and expect it to be

Yes.
 (2) It is against the very idea of operator overloading, which must have
     transparency to achieve its goal. That is, you must be able to forget

     the operator may be a method of an object. i==0 cant crash, so

     shouldn't be able to crash too.

Yes.
 (3) Any crash is bad.

Only true for crashes in critical production systems, where malfunction can be tolerable but downtime can not. Otherwise, as Walter also noted, crashes are usually better than undetected malfunction. Cheers, Luna Kid
Mar 31 2003
prev sibling next sibling parent reply "Carlos Santander B." <carlos8294 msn.com> writes:
"Helmut Leitner" <helmut.leitner chello.at> escribió en el mensaje
news:3E87DEA7.A5ABC430 chello.at...
|
| (2) It is against the very idea of operator overloading, which must have
|     transparency to achieve its goal. That is, you must be able to forget
that
|     the operator may be a method of an object. i==0 cant crash, so
null==object
|     shouldn't be able to crash too.
|

I don't understand much of what you guys have said about all this, but
here's what I think.
I don't want this to happen:

Object o1,o2;
...
if (o1==o2)   //access violation because o1 is null

I just hate it. But under a different POV, it makes sense. o1==o2 becomes
o1.eq(o2) (or is it cmp?), which isn't possible because o1 is null, so we
can't reference a function of a null object. That's all I can say.

-------------------------
Carlos Santander


---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.465 / Virus Database: 263 - Release Date: 2003-03-25
Mar 31 2003
parent reply Helmut Leitner <helmut.leitner chello.at> writes:
"Carlos Santander B." wrote:
 
 "Helmut Leitner" <helmut.leitner chello.at> escribió en el mensaje
 news:3E87DEA7.A5ABC430 chello.at...
 |
 | (2) It is against the very idea of operator overloading, which must have
 |     transparency to achieve its goal. That is, you must be able to forget
 that
 |     the operator may be a method of an object. i==0 cant crash, so
 null==object
 |     shouldn't be able to crash too.
 |
 
 I don't understand much of what you guys have said about all this, but
 here's what I think.
 I don't want this to happen:
 
 Object o1,o2;
 ...
 if (o1==o2)   //access violation because o1 is null
 
 I just hate it. But under a different POV, it makes sense. o1==o2 becomes
 o1.eq(o2) (or is it cmp?), which isn't possible because o1 is null, so we
 can't reference a function of a null object. That's all I can say.

I think you first impression is right. The other POV uses intimate knowledge about implementation detail. It's understandable like many weird things in programming languages and APIs, but it is a violation of common sense. -- Helmut Leitner leitner hls.via.at Graz, Austria www.hls-software.com
Mar 31 2003
parent reply Jonathan Andrew <Jonathan_member pathlink.com> writes:
Hmm, 
Well let me first say that I love the current == and === operators, They
are probably my single favorite aspect of D, especially compared to Java. The
null issue is a pain, and I've been thinking about how to get around it.
How about a static object called "null", which for all practical purposes would
act just as a null pointer, but could have its own methods like eq(), so null
pointer exceptions wouldn't arise under comparison. 

//somewhere in memory,
Object null;

//in our program
Object o1, o2;   //by default, &o1 == &null, &o2 == &null

if(o1 == o2)   //no longer an access violation, since both references are
//pointing to a valid object. 

myObj = null;  //now just points to the null object, which is valid.

To make life simpler, classinfo, toString(), etc. would all just return "null",
to aid in debugging.

I'm no language designer or theorist, but this seemed to me to be an easy way
to avoid those nasty crashes but still be assured that you will know if an
object is uninitialized. Whether or not this is a kludge is up to you guys. =)
Do any other languages do this?

-Jon

 I don't understand much of what you guys have said about all this, but
 here's what I think.
 I don't want this to happen:
 
 Object o1,o2;
 ...
 if (o1==o2)   //access violation because o1 is null
 
 I just hate it. But under a different POV, it makes sense. o1==o2 becomes
 o1.eq(o2) (or is it cmp?), which isn't possible because o1 is null, so we
 can't reference a function of a null object. That's all I can say.


Mar 31 2003
next sibling parent Karl Bochert <kbochert copper.net> writes:
On Mon, 31 Mar 2003 23:15:00 +0000 (UTC), Jonathan Andrew
<Jonathan_member pathlink.com> wrote:
 
 //in our program
 Object o1, o2;   //by default, &o1 == &null, &o2 == &null
 
 if(o1 == o2)   //no longer an access violation, since both references are
 //pointing to a valid object. 
 
 myObj = null;  //now just points to the null object, which is valid.
 
 To make life simpler, classinfo, toString(), etc. would all just return "null",
 to aid in debugging.
 
 I'm no language designer or theorist, but this seemed to me to be an easy way
 to avoid those nasty crashes but still be assured that you will know if an
 object is uninitialized. Whether or not this is a kludge is up to you guys. =)
 Do any other languages do this?
 
 -Jon
 

Simple, rational, elegant -- it'll never fly. Karl Bochert
Apr 01 2003
prev sibling parent reply Burton Radons <loth users.sourceforge.net> writes:
Jonathan Andrew wrote:
 //somewhere in memory,
 Object null;
 
 //in our program
 Object o1, o2;   //by default, &o1 == &null, &o2 == &null
 
 if(o1 == o2)   //no longer an access violation, since both references are
 //pointing to a valid object. 
 
 myObj = null;  //now just points to the null object, which is valid.
 
 To make life simpler, classinfo, toString(), etc. would all just return "null",
 to aid in debugging.
 
 I'm no language designer or theorist, but this seemed to me to be an easy way
 to avoid those nasty crashes but still be assured that you will know if an
 object is uninitialized. Whether or not this is a kludge is up to you guys. =)
 Do any other languages do this?

Object Null = new Object; Foobar bar = Null; Null is not a Foobar. It saves this specific case, but putting incorrect objects in cells is a good way to get bad debug sessions. At least with ((void*) 0) we can catch bad method calls in the invariant contract. Also, null has multiple types - it's an object reference, pointer, and empty array. Has anyone any reasons for not doing the translation method: a === b || (a !== null && b !== null && a.eq (b)) Because I think what Matthew's eqi suggestion is going to lead to is an eqi implementation in Object that is identical to this and is almost never overloaded - so a little slower than doing it directly and involving obscure functionality that'll like as not just confuse.
Apr 01 2003
parent reply "Matthew Wilson" <dmd synesis.com.au> writes:
 Has anyone any reasons for not doing the translation method:

     a === b || (a !== null && b !== null && a.eq (b))

 Because I think what Matthew's eqi suggestion is going to lead to is an
 eqi implementation in Object that is identical to this and is almost
 never overloaded - so a little slower than doing it directly and
 involving obscure functionality that'll like as not just confuse.

Why is it slower than doing directly? Surely it'll inline. And please bear in mind that I said it would be an explicit part of the language that the compiler gets to choose whether eq() or eqi() gets called, which in my estimation will mean it'll probably be more efficient. (Walter's quite good at writing optimising compiler's, is he not?) As I've said earlier, if it was up to me I'd just ditch the eqi() and have that part implicit. The compiler can do whatever it likes with == expressions, but should it need to call your eq() you are guaranteed to be talking to two (whether one is "this", or it's static) non-null object instances.
Apr 01 2003
parent reply Burton Radons <loth users.sourceforge.net> writes:
Matthew Wilson wrote:
Has anyone any reasons for not doing the translation method:

    a === b || (a !== null && b !== null && a.eq (b))

Because I think what Matthew's eqi suggestion is going to lead to is an
eqi implementation in Object that is identical to this and is almost
never overloaded - so a little slower than doing it directly and
involving obscure functionality that'll like as not just confuse.

Why is it slower than doing directly? Surely it'll inline. And please bear in mind that I said it would be an explicit part of the language that the compiler gets to choose whether eq() or eqi() gets called, which in my estimation will mean it'll probably be more efficient. (Walter's quite good at writing optimising compiler's, is he not?)

You can't inline virtual methods. Unless if it's final, it'll be a minor speed burden, and if it is final, then it shouldn't be polluting Object. In any case we're not talking about an overhead worth caring much about. The big problem is that eqi is a Babel feature; I don't want to spend hours scanning through code to finally realise that the programmer overloaded eqi with some new semantics.
Apr 02 2003
parent "Matthew Wilson" <dmd synesis.com.au> writes:
Then let's dispense with eqi(). I am *very* happy to do so.

That way we can completely leave it to the compiler, with maximal
efficiency.


"Burton Radons" <loth users.sourceforge.net> wrote in message
news:b6g21o$28g5$1 digitaldaemon.com...
 Matthew Wilson wrote:
Has anyone any reasons for not doing the translation method:

    a === b || (a !== null && b !== null && a.eq (b))

Because I think what Matthew's eqi suggestion is going to lead to is an
eqi implementation in Object that is identical to this and is almost
never overloaded - so a little slower than doing it directly and
involving obscure functionality that'll like as not just confuse.

Why is it slower than doing directly? Surely it'll inline. And please


 in mind that I said it would be an explicit part of the language that


 compiler gets to choose whether eq() or eqi() gets called, which in my
 estimation will mean it'll probably be more efficient. (Walter's quite


 at writing optimising compiler's, is he not?)

You can't inline virtual methods. Unless if it's final, it'll be a minor speed burden, and if it is final, then it shouldn't be polluting Object. In any case we're not talking about an overhead worth caring much about. The big problem is that eqi is a Babel feature; I don't want to spend hours scanning through code to finally realise that the programmer overloaded eqi with some new semantics.

Apr 02 2003
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Helmut Leitner" <helmut.leitner chello.at> wrote in message
news:3E87DEA7.A5ABC430 chello.at...
 (3) Any crash is bad. If there is a sensible way to continue operation in
     a situation we should take the chance to tolerate it. A "close" on a

     that is not open - fine, lets go on. A free on some pointer that is

     - whats the problem? An object that doesn't exist compared to null in

     well sure it should return "true". Nothing else would make more sense.

Crashes, sure. But what is happening here is an exception is thrown, not necessarilly a crash. A null pointer reference throws an exception, which can be caught like any other exception. The philosophy in D is that errors should be reported by throwing exceptions, not by returning error codes. If there was any error in my program logic, I'd much rather have an exception promptly thrown. That gives me the best opportunity to debug it, or if I want something else to happen, I can catch that exception and soldier on. (I view a crash as more like the program corrupted its own data or the operating system's. In MSDOS, a crash usually meant scrambling the entire system. Hardware generated exceptions were a huge advance.) So, think of (o==p) with objects as "Compare the contents of object o with object p. If either is null, there are no contents, and that is outside the scope of the == operator's purpose. Therefore, an exception is thrown just as if an array bounds were exceeded. If the object references are not supposed to be null, then it's a program bug. If the object references can be null, then explicitly code what a null reference means."
Jun 13 2003