March 28, 2003
Heh, sooner or later we have to agree on something.  Don't we? :-)

Anyway, here's what I think:

> > Okay, I love C++ but something is entirely wrong if such an antiquated language manages to provide the most reliable solution to something as
> basic
> > as this (at least if you're not a newbie).
>
> Why do you say it's basic? C#, D & Java all attempt to fudge the
dichotomous
> nature of instances and their pointers for the sake of a (what some would say specious) syntactic nicety. For the record, I'm not one of them in principle, but no language other than C++ has yet managed this distinction in a sensible and complete fashion. D is coming the closest, but if we
have
> to manually test for identity before we can test for equality then all
that
> syntactic nicety is just a waste of time.
>
> Whatever anyone's opinion, I fail to see how this is a simple issue.

Basic because it's a problem that even simple programs could get bitten by. Basic, but definitely not simple.

> > I think anything we come up with is just going to be a kludge around a fundamental problem that is created by not automatically initializing objects when they are declared.
>
> Whether you like it or not, a big reason that C, C++ and D have been designed the way they have is for efficiency. C & C++ owe a lot of their sucecss to this. You're not going to sell D as anything other than a lame puppy if it cannot demonstrate similar, or better, levels of efficiency
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.

C++ also happens to be the only member of the bunch that does initialize instances automatically.  It's no accident that the equality identity problem is a non-issue in C++.

> > I wonder if it wouldn't be better to have the language implicitly create
> an
> > instance of the object when it is declared if it's not done explicitly:
> > blah blab;
> > blah blab=new blah;    //these to lines are equivalent
> > //if blab is global, the instance would be created before main is called
>
> [Q: I presume that D currently initialises an uninitialised variable to null. Is this correct? If it doesn't, it should.]

Yeah, it does.

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

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

> > You could then say that it is illegal to assign null to an object
> reference.
> > I don't think it makes much sense to have a null object reference most
of
> > the time anyhow.
>
> I have to strongly disagree with this, sorry. :)
>
> null is a _very_ useful state. Without it we'd either have to have signal objects, which is inefficient, or use exceptions for nearly everything.
> >  And it is easy to add a "bit empty;" line to a class for
> > the few times when something like that might be useful.

:-)  Yeah, a bit empty field may not be the most brilliant solution.

> > This would avoid the problem completely.  This could in theory cause a performance hit, but I think that in practice that would happen seldom enough that it would be more than worth the robustness.  How often do
you
> > declare something and don't instantiate it almost immediately?  Even
when
> > you do, it's likely to be a one time only thing--that is to say, it's probably not going to be something that happens over and over again
inside
> a
> > loop or recursive function.
> >
> >
>
>
> All of the trouble so far is based on a desire to handle the mess of equivalence/identity in languages that use references as pointers (i.e.
the
> references can be null). Unless there is an efficient built-in, but potentially user-tailorable, handling of this then we might as well get
rid
> of the == and === operators entirely, and do everything with
> Object.Equals(o1, o2) and Object.EqualsReference(o1, o2) methods, with all
> the glamour that would entail.
>

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.


March 28, 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.


March 28, 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.


March 28, 2003
I agree that access violations have their place, and make use of them on occasion, but disagree that this is one.

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

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

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

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

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

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

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

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

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

Major wart. Sorry.

"Walter" <walter@digitalmars.com> wrote in message news:b60uqc$7pi$1@digitaldaemon.com...
>
> "Benji Smith" <Benji_member@pathlink.com> wrote in message news:b5ven6$233d$1@digitaldaemon.com...
> > I wholeheartedly agree. I need to be able to trust that I can use == and
> ===
> > without worrying about access violations for null pointers.
>
> I'm going to disagree with a couple points. First of all, I'm going to disagree with the notion that an access violation is something bad. I
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.
>
>


March 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

March 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.
>
>Bill

I think you are right.  It really makes more sense for the operator overloading functions to be module level.

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



March 28, 2003
Patrick Down wrote:
> In article <3E84520F.8080809@viasic.com>, Bill Cox says...
> 
>>Ok, I'll kick in with a very very old point of view on the topic that dates back to the beginning of time...
>>
>>Operator overloading should not be implemented as methods.  They should be implemented as overloaded functions at the module level.  Then, this whole problem goes away.  Besides, mathematically, a binary operator's operands have equal status.  Why pass one to a method of the other? It's just a stupid trick to try to avoid writing module-level routines.
>>
>>The fact that we can't even define the == operator without worrying about the LEFT operand being null (but not the right) clearly shows the flaw in C++ style operator overloading.
>>
>>Bill
> 
> 
> I think you are right.  It really makes more sense for the operator overloading
> functions to be module level.
> 
> From a historical D perspective, I think that the reason they were implemented
> as member fuctions instead of module level functions was the issue of accessing
> private class members.  This was before everything in a module had public
> access.  Now that this is not an issue any more I would certainly be the first
> to vote for them to be changed.

The current setup also allows one to make the comparison method virtual. (I'm not sure how useful that is, but it's there)

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


March 28, 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
>


March 28, 2003
"Matthew Wilson" <dmd@synesis.com.au> wrote in message news:b61875$epf$1@digitaldaemon.com...
> D, like C# and Java, "pretends" that a pointer to an instance is a "thing" that can be treated like the instance itself. This is evident in the fact that we wish to, and are able to, apply == to references.
>
> This pretence is a dishonesty, one which is obvious when one considers how C++ does it. However, when a language does not deal with pointers, the dishonesty can be benign and useful.
>
> I don't have a problem with this pretence, so long as it's consistent. If you want objects to be treated "like the ints", then you have to do it fully. This argument is well worn wrt C++ operator overloading issues, but applies all round.
>
> When I write code that compares two entities that are int, I don't have to write
>
> int    i1 = . . .
> int    i2 = . . .
>
> if( i1 !== null &&
>     i2 !== null &&
>     i1 == i2)
> {
>
> That would be ludicrous. So why should I have to do it with objects?
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.

I'll argue that value semantics and reference semantics are fundamentally different, despite having a common syntax. I don't think there's any getting away from that. Objects are accessed by reference, not by value, and thinking of them as having value semantics (like ints in your example) just leads to disaster in many more ways than the behavior of == and === (for example, consider passing function arguments by value and by reference and the semantic differences).

> Furthermore, since D has templates - you'll have to bear with me on this one, as I'm a real neophyte with D's template syntax - is it not likely
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.

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.