D - null == o?
- Matthew Wilson (40/40) Mar 25 2003 In the documentation it says that the expression (a == b) is rewritten a...
- Daniel Yokomiso (23/63) Mar 26 2003 If
- Matthew Wilson (98/170) Mar 26 2003 Understood.
- Andy Friesen (6/225) Mar 26 2003 Maybe this has been suggested before, but this could all be avoided by
- Burton Radons (5/5) Mar 26 2003 This could be more easily fixed by fixing == semantics. When a and b
- Benji Smith (5/185) Mar 27 2003 I wholeheartedly agree. I need to be able to trust that I can use == and...
- Matthew Wilson (26/242) Mar 27 2003 Very much agreed. (Until I come across something else) this is the only
- Walter (21/23) Mar 27 2003 ===
- Matthew Wilson (52/75) Mar 28 2003 I agree that access violations have their place, and make use of them on
- Bill Cox (11/11) Mar 28 2003 Ok, I'll kick in with a very very old point of view on the topic that
- Patrick Down (8/19) Mar 28 2003 I think you are right. It really makes more sense for the operator over...
- Andy Friesen (3/29) Mar 28 2003 The current setup also allows one to make the comparison method virtual....
- Sean L. Palmer (28/39) Mar 28 2003 You have got to the root of the problem, Bill.
- Bill Cox (10/65) Mar 28 2003 I have to agree with all comments by Sean, Andy, and Patrick. Like
- Matthew Wilson (17/60) Mar 28 2003 Agreed.
- Walter (6/14) May 16 2003 The reason the operator overloads are class members is so they can be
- C. Sauls (12/13) May 17 2003 overloads.
- Walter (4/17) May 18 2003 I didn't like the "operator+" notation because of the problem with the
- Ilya Minkov (10/11) May 17 2003 As you mention that... maybe.
- Giancarlo Bellido (11/22) May 17 2003 Personally, I think that operator overloading should be something like i...
- Sean L. Palmer (15/41) May 18 2003 I agree. I like C++ operator overloading better. It bugs me that if I ...
- Ilya Minkov (14/27) May 18 2003 It shall make writing other tools which process D code more difficult.
- Giancarlo Bellido (20/47) May 18 2003 ok, what about declaring the operator like a function without the operat...
- Ilya Minkov (12/20) May 18 2003 Yikes, UGLY! The problem with it is that you cannot grep for it. In C++
- Andy Friesen (10/24) May 18 2003 Except for shl_r and shr_r :)
- Walter (8/13) May 18 2003 To me, being able to grep for it is a big deal. I also know where to loo...
- Matthew Wilson (10/10) May 20 2003 I think the python style - widely recognised and greppable names - is
- Matthew Wilson (11/12) May 20 2003 Why should we think OO? One of the reasons that C++ is fantastic (and wi...
- Walter (20/32) May 18 2003 want
- Sean L. Palmer (24/56) May 19 2003 What problem?
- Walter (13/36) May 28 2003 I
- Sean L. Palmer (21/47) May 29 2003 if
- Mark T (4/10) May 29 2003 I agree, fake classes to "encapsulate" all functions like Java is bad.
- Sean L. Palmer (17/29) Jun 01 2003 It seems like it would be a good idea to unify all function and method c...
- Matthew Wilson (7/14) May 29 2003 in
- Matthew Wilson (9/41) May 20 2003 in
- Matthew Wilson (12/56) May 20 2003 Couldn't agree more.
- Georg Wrede (19/43) May 21 2003 Now, having:
- Helmut Leitner (15/74) May 21 2003 Wouldn't it be nicer to have an enum property .hash returning a constant...
- Sean L. Palmer (11/57) May 22 2003 Simply have .toString() propertys on enum members. If they are never us...
- Matthew Wilson (11/22) Mar 28 2003 Bill, I think you're agreeing with what I'm saying, sort of. I suggested
- Bill Cox (4/35) Mar 28 2003 You're right. My mistake, I just forgot. For some reason, I haven't
- Walter (17/51) Mar 28 2003 Object
- Mike Wynn (9/25) Mar 31 2003 worked
- Ilya Minkov (16/17) Apr 09 2003 No. Taken C++:
- Mike Wynn (13/29) Apr 09 2003 not sure what you thing I've not bought ?
- Matthew Wilson (2/4) Apr 09 2003 You've got me sold.
- Jon Allen (22/27) Mar 27 2003 Okay, I love C++ but something is entirely wrong if such an antiquated
- Matthew Wilson (75/103) Mar 27 2003 basic
- Daniel Yokomiso (167/260) Mar 27 2003 Hi,
- Matthew Wilson (107/147) Mar 28 2003 Man, this is all too nasty. Also, I'm confused by a lot of what is being
- Ilya Minkov (20/34) Mar 28 2003 Wait! It is not necessarily inconsistent. You forget that in D an Object...
- Matthew Wilson (5/8) Mar 28 2003 I can't take it seriously. Mate, I _really_ mean no offence, but I have
- Jon Allen (27/57) Mar 28 2003 Contracts _are_ great. The world _is_ full of liars. I think there a mo...
- Ilya Minkov (8/17) Mar 28 2003 It's rather the opposite: in D objects supposed to be "just objects",
- Jon Allen (51/66) Mar 28 2003 so
- Sean L. Palmer (29/97) Mar 29 2003 I'm all for C++'s variable allocation technique.
- Matthew Wilson (99/111) Mar 28 2003 For me also. However, given the choice between ugliness and
- Luna Kid (47/47) Mar 29 2003 Matthew, I followed through this lively thread (not 100%
- Luna Kid (2/3) Mar 29 2003 Sorry, I meant: prevent creating zero references,
- Antti Sykari (6/13) Mar 29 2003 My impression is that contracts bring the crash to precisely where the
- Antti Sykari (59/59) Mar 29 2003 I found the discussion about references confusing, so here's a short
- Antti Sykari (106/106) Mar 29 2003 IMO, "==" should mean value equality for value types and reference
- Luna Kid (10/18) Mar 29 2003 Oops, I just tried it:
- Matthew Wilson (96/143) Mar 29 2003 Being quite thick in the head when it comes to reading - classic
- Luna Kid (7/11) Mar 29 2003 Sorry about driving you into that... But anyhow,
- Antti Sykari (61/61) Mar 29 2003 While I was writing the previous article, it occurred to me that
- Antti Sykari (30/52) Mar 29 2003 Here we might also get "an impossible situation" easily.
-
Karl Bochert
(28/28)
Mar 28 2003
On Wed, 26 Mar 2003 15:17:36 +1100, "Matthew Wilson"
- Derek Parnell (23/37) Mar 30 2003 I have a similar point of view to Karl on this. Conceptually, I regard b...
- Matthew Wilson (11/50) Mar 30 2003 Derek
- Derek Parnell (33/39) Mar 30 2003 Yeah, I know ;-)
- Helmut Leitner (16/23) Mar 30 2003 Then I'll add my three reasons why it shouldn't crash.
- Luna Kid (13/21) Mar 31 2003 save)
-
Carlos Santander B.
(25/25)
Mar 31 2003
"Helmut Leitner"
escribió en el mensaje - Helmut Leitner (7/30) Mar 31 2003 I think you first impression is right. The other POV uses intimate knowl...
- Jonathan Andrew (21/32) Mar 31 2003 Hmm,
- Karl Bochert (3/22) Apr 01 2003 Simple, rational, elegant -- it'll never fly.
- Burton Radons (14/32) Apr 01 2003 Object Null = new Object;
- Matthew Wilson (10/16) Apr 01 2003 Why is it slower than doing directly? Surely it'll inline. And please be...
- Burton Radons (8/22) Apr 02 2003 You can't inline virtual methods. Unless if it's final, it'll be a
- Matthew Wilson (8/30) Apr 02 2003 Then let's dispense with eqi(). I am *very* happy to do so.
- Walter (23/28) Jun 13 2003 file
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
"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?Ifnot 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 Imean.)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
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. 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...asIn the documentation it says that the expression (a == b) is rewrittenaa.equals(b). Given that, what happens if a is null? NullPointerException, or is thereinside"safe-this" mechanism, ie. a.this is automatically checked against null?Ifnot 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 Imean.)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" referencemethods, 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
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. 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...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."Matthew Wilson" <dmd synesis.com.au> escreveu na mensagem news:b5r9l2$2r2u$1 digitaldaemon.com...asIn the documentation it says that the expression (a == b) is rewrittenaa.equals(b). Given that, what happens if a is null? NullPointerException, or is thereinside"safe-this" mechanism, ie. a.this is automatically checked against null?Ifnot 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 Imean.)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" referencemethods, 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
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
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. 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...asIn the documentation it says that the expression (a == b) is rewrittenaa.equals(b). Given that, what happens if a is null? NullPointerException, or is thereinside"safe-this" mechanism, ie. a.this is automatically checked against null?Ifnot 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 Imean.)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" referencemethods, 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
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...theUnderstood. However, I have to strongly disagree with you - or is it Walter? - thatleavesD 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.ofone clueless as to whether any reference comparisons are comparing equivalence or identity. This also sucks, unless one "trusts" the author"Identity &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 theForequivalence" 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().couldmy part, though, that'd be gross. So: It seems that we must find a way to sensibly incorporate === in ==. Weidentityengineer the compiler, or mandate the class author, to incorporatewhichchecks in equivalence comparisons. However, there are circumstances inthethe 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,donecompiler provides "a bit compare of the contents of the two structs isimplementationto 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()== ywould 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 xthisas 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 witheq()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-staticrewrittenas 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) isthereasa.equals(b). Given that, what happens if a is null? NullPointerException, or isnull?a"safe-this" mechanism, ie. a.this is automatically checked againstcrash?Ifnot 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 Imean.)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) -doAccess violation. It's safer to use a generic equals function that willthe 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" referenceinsidemethods, 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
"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
I agree that access violations have their place, and make use of them on occasion, but disagree that this is one. 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 _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 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...workedI 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. Ifor 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*ofthe 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 outsucha check, you're implicitly documenting that it cannot be null.
Mar 28 2003
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
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. BillI 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
Patrick Down wrote:In article <3E84520F.8080809 viasic.com>, Bill Cox says...The current setup also allows one to make the comparison method virtual. (I'm not sure how useful that is, but it's there)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. BillI 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
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
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
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 nomodule-scope("free") operators are possible. You're wrong about C++. It has these. They are good. Preferred in facttomaking the binary operator part of the class. The only reason you'd want a binary operator to be part of a class is ifitneeded access to some private section. In C++ you use friend for this;inD 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 declaringoperators.You have to use some specially named member function currently, whichmeansyou 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 becausememberadd 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
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 benamed.Your example would then be BigInt __op_add__ (BigInt a, BigInt b) {
May 16 2003
It might be a good idea to do an "op" prefix to all the operatoroverloads. 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
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 operatoroverloads. 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
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
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:overloads.It might be a good idea to do an "op" prefix to all the operatorAs 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
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:overloads.It might be a good idea to do an "op" prefix to all the operatorAs 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
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
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:wantI agree. I like C++ operator overloading better. It bugs me that if IWYSIWYGa '+' I have to type 'add'; I really cannot see the problem with acan'tsystem. 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 youeithermake 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 ownsomeof the classes but may still want to provide an overloaded operator fortoreason.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 seemoperatorbe any real reason why not, except that this way allows operators to be virtual, which I say is hogwash since you could always define yourclass.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 mathematicThink OO people. I feel like changing to Sather. -i.
May 18 2003
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
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. giancarloExcept for shl_r and shr_r :) class MyClass { Stream shl_r(Stream s) { doStuffWith(s); return s; } }
May 18 2003
"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
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
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 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
"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 Iwanta '+' I have to type 'add'; I really cannot see the problem with aWYSIWYGsystem. 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 youcan'tmake 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 owneitherof the classes but may still want to provide an overloaded operator forsomereason. (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 seemtobe any real reason why not, except that this way allows operators to be virtual, which I say is hogwash since you could always define youroperatorto 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
"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...What problem? float operator-(int a, float b) { return float(a) - b; } float operator-(float a, int b) { return a - float(b); }I agree. I like C++ operator overloading better. It bugs me that if Iwanta '+' I have to type 'add'; I really cannot see the problem with aWYSIWYGsystem. It's not that much more work in the parser, is it?The problem comes with the reverse operators, such as divr.inThe inability to make free operators is really limiting. It means youcan'tmake 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 owneitherof the classes but may still want to provide an overloaded operator forsomereason. (What if someone invents a cool STL trick that works like that,see above.the future...? C++ will be able to do it, D won't.) There doesn't seemtobe any real reason why not, except that this way allows operators to be virtual, which I say is hogwash since you could always define youroperatorto 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 >>.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 anewclass 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
"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...I"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 ifThat doesn't work if you want to make them virtual.wantWhat problem? float operator-(int a, float b) { return float(a) - b; } float operator-(float a, int b) { return a - float(b); }a '+' I have to type 'add'; I really cannot see the problem with aWYSIWYGsystem. It's not that much more work in the parser, is it?The problem comes with the reverse operators, such as divr.different2) its ability to produce operator overloads that have bizarrelyinsemantics with the operator, such as the iostream << and >>.This is a religious issue, which makes me think this will never be solvedD to my liking.Perhaps.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.3) you need to write, in general, twice as much code in C++ to define anewclass with operators than you do in D.Example?<g>Not that I have a strong opinion about it, or anything <g>.None of us do. ;)
May 28 2003
"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...if"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 thatIWhat 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.That doesn't work if you want to make them virtual.wantWhat problem? float operator-(int a, float b) { return float(a) - b; } float operator-(float a, int b) { return a - float(b); }a '+' I have to type 'add'; I really cannot see the problem with aWYSIWYGsystem. It's not that much more work in the parser, is it?The problem comes with the reverse operators, such as divr.a3) you need to write, in general, twice as much code in C++ to defineWhat 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 { } SeannewIn 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.class with operators than you do in D.Example?
May 29 2003
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
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...operators.I don't understand the current stance on prohibition of nonmemberinI 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"Noweither 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
I don't understand the current stance on prohibition of nonmemberoperators.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"ineither class.Agreed.Look at Java. They made it so every function has to be a class member.Nowpeople have to make classes they don't need.Agreed. So, in summary, agreed!
May 29 2003
"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...inI agree. I like C++ operator overloading better. It bugs me that if Iwanta '+' I have to type 'add'; I really cannot see the problem with aWYSIWYGsystem. 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 youcan'tmake 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 owneitherof the classes but may still want to provide an overloaded operator forsomereason. (What if someone invents a cool STL trick that works like that,Disagree. This in convenience is well worth the fact that operators are free, rather than membersthe future...? C++ will be able to do it, D won't.) There doesn't seemtobe any real reason why not, except that this way allows operators to be virtual, which I say is hogwash since you could always define youroperatorto 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 >>.Agree, MASSIVELY. Those chevrons and the iostreams are a wart and a curse3) you need to write, in general, twice as much code in C++ to define anewclass 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 ruleNot that I have a strong opinion about it, or anything <g>.
May 20 2003
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 Iwanta '+' I have to type 'add'; I really cannot see the problem with aWYSIWYGsystem. It's not that much more work in the parser, is it? The inability to make free operators is really limiting. It means youcan'tmake 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 owneitherof the classes but may still want to provide an overloaded operator forsomereason. (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 seemtobe any real reason why not, except that this way allows operators to be virtual, which I say is hogwash since you could always define youroperatorto 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...inPersonally, I think that operator overloading should be something likelikeC++. 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:overloads.It might be a good idea to do an "op" prefix to all the operatorAs 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
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
Georg Wrede wrote:From the specs: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.comArrays 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
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:all,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, afterknown at compile time? Isn't this the most common idiom to use near the definition of enums? Itmightbe overkill to have the enum statement implicitly define a COLORS structwiththe strings defined, but isn't this common enough to deserve a specialsyntax,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 twotimes.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 ofsyncsooner or later.
May 22 2003
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
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
"Matthew Wilson" <dmd synesis.com.au> wrote in message news:b61875$epf$1 digitaldaemon.com...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?Objectreferences should either consistently behave as built-ins in expressions,orthey 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 shouldeschewthe syntactic convenience and just accept that we have to use Objectmethodcalls 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 likelythatwe'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 referenceswouldbe 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
"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...workedI 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. Ifor 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*ofthe 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
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
"Ilya Minkov" <midiclub 8ung.at> wrote in message news:b71res$1541$1 digitaldaemon.com...Mike Wynn wrote:not sure what you thing I've not bought ? 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)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.
Apr 09 2003
should null.eq(null) [null = = null] be true or throw an exception?true, becauselanguage should be driven by ... semantics and ... robustnessYou've got me sold.
Apr 09 2003
This seems to be the worst of all possible worlds. D's bitten by its own cleverness here. Consider:Java sucks C++ is entirely straightforwardOkay, 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
"Jon Allen" <jallen minotstateu.edu> wrote in message news:b603qc$2ljo$1 digitaldaemon.com...basicThis seems to be the worst of all possible worlds. D's bitten by its own cleverness here. Consider:Java sucks C++ is entirely straightforwardOkay, I love C++ but something is entirely wrong if such an antiquated language manages to provide the most reliable solution to something asas this (at least if you're not a newbie).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 createaninstance 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 objectreference.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 insidealoop 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
Heh, sooner or later we have to agree on something. Don't we? :-) Anyway, here's what I think:dichotomousOkay, I love C++ but something is entirely wrong if such an antiquated language manages to provide the most reliable solution to something asbasicas this (at least if you're not a newbie).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 wehaveto manually test for identity before we can test for equality then allthatsyntactic 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.thanI 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 efficiencyits forebears/peers. The work Walter and I have been doing in the lastweekor so on performance comparisons has shown that D does indeed kick thearseof most of its peers in a number of areas; being a C++ diehard myself,thisis a significant factor in my personnally starting to think more and moreofusing 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++.Yeah, it does.I wonder if it wouldn't be better to have the language implicitly createaninstance 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 insomehorrible 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 havetohave multiple return statements - which is not my style - or I have cannot return null so must use exceptions - which may not beappropriate/desirable.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.ofYou could then say that it is illegal to assign null to an objectreference.I don't think it makes much sense to have a null object reference most:-) Yeah, a bit empty field may not be the most brilliant solution.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.youThis 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 dowhendeclare something and don't instantiate it almost immediately? Eveninsideyou 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 againatheloop 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 getridof 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
"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
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? - thattheD 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.one clueless as to whether any reference comparisons are comparing equivalence or identity. This also sucks, unless one "trusts" the authorofa class. (Got to love that trust ... it never let's you down, eh?)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().Formy part, though, that'd be gross. So: It seems that we must find a way to sensibly incorporate === in ==. Wecouldengineer the compiler, or mandate the class author, to incorporateidentitychecks in equivalence comparisons. However, there are circumstances inwhichthe 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,thecompiler provides "a bit compare of the contents of the two structs isdoneto 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()implementationwould 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 ==yas a call to X.eqi(x, y) or to X.eqi(x, y), presumably based on what it knows about the context of the two references. This means that any user-defined implementations of eq() and eqi() should be written with this in mind, presumably meaning that eqi() would do anything other than the default as shown above, apart from logging or other benign things. Given static eq() and static eqi() we would no longer have non-static eq() as a special function, and could likely get rid of it. (I haven't thought about that part yet.) Sound ok? Comments appreciated. I certainly can't see how we can have access violations when using == in expressions!!Perhaps we should try to see this with D colored glasses. If I say this: boolean foo(Object bar, Object baz) in { assert(bar !== null); assert(baz !== null); } body { return bar == baz; } I "know" that it is correct. The contract defines that only when bar and baz are non-null something sensible will happen. If they're null foo can do anything it wants to (usually just throw an assertion error, but if contracts are disabled evil things can happen). OTOH if I say this: boolean foo(Object bar, Object baz) { return (bar === null) ? (bar === baz) : (bar == baz); } I "must" deal with the possibility of getting strange values for bar or baz, so if I don't think carefully I'm assuming something without any "proof" (e.g. contracts) about it. We know that if "a === b", a can be substituted by b anywhere. Also, we know that "a === b" implies "a == b". This is pretty much what we can say about equivalence (I don't even think that equivalence must be symmetric but I may be alone here). This can be expressed in terms of contracts in Object.eq(Object): public bit eq(Object other) in { assert(other !== null); } out(result) { if (this === other) { assert(result); } if (null === other) { // an evil subclass may relax the precondition, but nothing should be eq to null assert(!result); } } Working on a language with contracts requires some changes of habit. If we did something using defensive programming (e.g. accepting nulls and returning false) now we should use offensive programming (e.g. reject it as a programming error using contracts). Smalltalk has a Nil object representing null values that responds to every method with doesNotUnderstand (similar to NullPointerException or access violation), but you can make it answer to any method with a Nil value, propagating the nullness. It may be nice to have an answer that leads to less checks and simpler code (google for Null Object Pattern), but it can propagate errors silently, a la NaN in floating point operations. IMO calling "==" is just sugar for calling "eq", so default restrictions apply too. I want to make clear that this mistake is common. Perhaps 1/3 of my bugs in D are due to using "==" on null variables, instead of the correct checks/contracts, but the solution isn't make this bug magically disappear and silently introduce others. We must change our thinking habits when using D and make heavy use of contracts. While I agree that checking for "null" may be tedious, it's the D way of doing things (Eiffel works like this too: their libraries are full of "require other_not_void: other /= Void"). The Nice language ( http://nice.sourceforge.net ) has a better solution, letting null values only in types marked with "?" and requiring explicit checks. Perhaps I should write "Null pointers considered harmful" someday ;-)Matthew[snip] Best regards, Daniel Yokomiso. "I'm less of a neurotic perfectionist than I was. But I don't think that anyone who has done good work in their life isn't a perfectionist. You have to be." - John Cleese --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.463 / Virus Database: 262 - Release Date: 18/3/2003
Mar 27 2003
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 (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?Ifnot 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 Imean.)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
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
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
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 notonlycan 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 equivalenceexpressions.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 == onanythingbut 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 thelanguage succinct, fits with me largely C/C++ background,[break]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
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
"Ilya Minkov" <midiclub 8ung.at> wrote in message news:b62neg$1iai$1 digitaldaemon.com...Jon Allen wrote:soI 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 becomeideapopular lately. What is the rationale behind the object as referenceseemsas opposed to the C++ idea of objects as objects? To me the C++ wayrequiredmuch simpler and more consistent, even with the pointers that areThat'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.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.fromIt also seems like the C++ way has more potential for offloading a lot of the work that needs to be doneWhy 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.runtime to compile time, resulting in much better performance.What makes you think that? [-Eliza]
Mar 28 2003
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 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...entireJon Allen wrote:I have a question though, and I hope it's not to stupid:-). Thissosoproblem is created by the idea of object as references that has becomeideapopular lately. What is the rationale behind the object as referenceseemsas opposed to the C++ idea of objects as objects? To me the C++ wayrequiredmuch simpler and more consistent, even with the pointers that areThat'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 aboutfor 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.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 thestack,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.objectfromIt also seems like the C++ way has more potential for offloading a lot of the work that needs to be doneWhy 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 anruntime to compile time, resulting in much better performance.What makes you think that? [-Eliza]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 tothatstuff 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 likelyhoodofforgetting those "new"s at least once sometime in any decently sized project, no matter how seasoned you are. Bugs are performance problemstoo.I can also see it taking a lot off the hands of the GC, since it wouldmakeit much easier to free a good percentage of data. It's a lot faster tomovea stack pointer or the like than it is to mark and sweep the entire heap.
Mar 29 2003
Ugliness is a big turnoff in a language though. At least it is for me,andI suspect I'm not the only one. It's completely irrational, and I'm sureImiss 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 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++ wayhasmore 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 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 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 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. 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 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
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
- prevent creating dangling object referencesSorry, I meant: prevent creating zero references, most importantly.
Mar 29 2003
"Jon Allen" <jallen minotstateu.edu> writes: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). -AnttiC. 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.
Mar 29 2003
I found the discussion about references confusing, so here's a short summary of how different languages handle equality. I include Java, "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 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? - 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 - if it's a reference type: - "==" in C++ (if object is a reference or the thing itself) 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 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
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. too.) -Antti
Mar 29 2003
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
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?YesBut 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, blahIf 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 aboveOr, 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
... 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
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
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
On Wed, 26 Mar 2003 15:17:36 +1100, "Matthew Wilson" <dmd synesis.com.au> wrote:I am a great admirer of Walter's work (Ever since he personally answered 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? 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
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: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". -- DerekI am a great admirer of Walter's work (Ever since he personally answered 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?
Mar 30 2003
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>wrote:<dmd synesis.com.au>On Wed, 26 Mar 2003 15:17:36 +1100, "Matthew Wilson"bothwrote:I have a similar point of view to Karl on this. Conceptually, I regardI am a great admirer of Walter's work (Ever since he personally answered 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' 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
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. MatthewYeah, 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
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
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 OOprogrammingin C, C++ or Java, you will write exactly that (and expect it to besave) 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 forgetthatthe operator may be a method of an object. i==0 cant crash, sonull==objectshouldn'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
"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
"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
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? -JonI 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
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? -JonSimple, rational, elegant -- it'll never fly. Karl Bochert
Apr 01 2003
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
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
Matthew Wilson wrote: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.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?)
Apr 02 2003
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:bearHas 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 pleasethein mind that I said it would be an explicit part of the language thatgoodcompiler gets to choose whether eq() or eqi() gets called, which in my estimation will mean it'll probably be more efficient. (Walter's quiteat 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
"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 afilethat is not open - fine, lets go on. A free on some pointer that isNULL- whats the problem? An object that doesn't exist compared to null inD -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