April 18, 2008
On 18/04/2008, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> Huh?  I think maybe you meant C to inherit from B?

Oh yeah. That was a typo. Whoops! I should have said

    class C : B
    {
    }


>  I don't really agree that opEquals or opCmp should not be inherited.  I
>  think normal inheritance the way it is now is just fine.

You do? Why?

You obviously understand that if Z is a great-great-great-great-...-grandchild of A, then it will still use A's opCmp() function if not overridden? And you're OK with that?



>  You need to have opEquals in Object in order to support hashing.

Please could you elaborate, as I don't understand why this is so?


>  The default opCmp should be redefined to throw an exception.

Again, I ask, why? Why detect something at run-time that could be detected at compile time?

Moreover, if Z is a great-great-great-great-...-grandchild of A, then it will use A's opCmp() function - not Object's. Overriding opCmp even /once/ changes the defaut for the entire heirarchy from that point down, and at that point, Object's implementation becomes irrelevant.


>  An alternative
>  is that opCmp is defined in an interface.

Yes, that is an alternative. I had thought of that. But then I realised that orderedness can be detected at compile-time without need for an interface, so it seemed unnecessary.
April 18, 2008
On 18/04/2008, Yigal Chripun <yigal100@gmail.com> wrote:
>  with the above suggestion the opCmp would accept the above "obj" ot type
>  B since it has static type A.
>  is the above behavior acceptable? I'm not sure.

Good question.

>  for example, what if my code retrieves A's from a collection, so by
>  comparing only number and not name it can retrieve the wrong instance
>  from the collection(a real example would be a collection of widgets in a
>  GUI toolkit representing all the controls of a screen). the compiler
>  cannot check this at compile time so in order to prevent this the check
>  should happen at run-time and an exception should be thrown on error.

Actually, I don't think that would be a problem, for the following reason: the use of "explicit" would not be compulsory! So in the circumstance you describe, you'd just define an opCmp, and /not/ make it explicit. Then the normal rules of inheritance would apply.



>  D really needs a standard facility to add user defined annotations
>  (metadata) to code and instead of adding more keywords to D, the above
>  "explicit" would be a perfect candidate to be provided by the standard
>  library.

Since its purpose is to block inheritance, I'm not completely sure how a library could do that. But I do agree with you about needing a mechanism for such things. After all, we don't add a new keyword every time we add a function, so why should we need a new keyword for each new type-constructor or storage class? Someone else pointed out that the @ sign is unused in D, and could be used for annotations ... but that's an issue for another thread.
April 18, 2008
On 18/04/2008, Jason House <jason.james.house@gmail.com> wrote:
>  I'm sure you could say that foo must now declare its parameters as explicit, but requiring all functions that compare A's to have explicit parameters really starts to kill the whole utility of having a base class to begin with.

That's a good point, but again, nothing /requires/ explicit parameters. It's like anything else - if you desire the behavior, use it; if you don't, don't.

But that is a very good observation, yes. Well spotted.
April 18, 2008
"Janice Caron" wrote
> On 18/04/2008, Steven Schveighoffer wrote:
>>  I don't really agree that opEquals or opCmp should not be inherited.  I
>>  think normal inheritance the way it is now is just fine.
>
> You do? Why?
>
> You obviously understand that if Z is a great-great-great-great-...-grandchild of A, then it will still use A's opCmp() function if not overridden? And you're OK with that?

Yes.  That is a choice of the great-great-great-...-grandchild of A's author :)  By not overriding opEquals or opCmp, he is saying that if you compare it with something else, A's implementation is the one to use.  If he wanted to specify that opCmp should be different he would have rewritten opCmp.

>>  You need to have opEquals in Object in order to support hashing.
>
> Please could you elaborate, as I don't understand why this is so?

A hashtable works by putting objects into buckets based on a hash-code. Usually you mod the hash-code so it fits in an array.

At that point, you have to deal with collisions.  So a simple method is to use a linked list for each bucket.

To look-up that object, you need to traverse the list, finding the object that equals the input.

So for example, an int[int], to lookup the int, it first converts to a hash-code, finds the right bucket, then traverses the linked list looking for the int equal to the key.  Then it returns the value.  So for anything to be a key, it must be able to provide a hash, and you must be able to check for equality.

>>  The default opCmp should be redefined to throw an exception.
>
> Again, I ask, why? Why detect something at run-time that could be detected at compile time?

I think the better solution is to use the interface.  But if that is not acceptable, at the very least, the default implementation should throw an exception.

>
> Moreover, if Z is a great-great-great-great-...-grandchild of A, then it will use A's opCmp() function - not Object's. Overriding opCmp even /once/ changes the defaut for the entire heirarchy from that point down, and at that point, Object's implementation becomes irrelevant.

The only benefit I see is that you would not have to declare that an object implements the interface.  Personally I don't think it's worth it, but I could live with it.  The way it is now is completely wrong.

>>  An alternative
>>  is that opCmp is defined in an interface.
>
> Yes, that is an alternative. I had thought of that. But then I realised that orderedness can be detected at compile-time without need for an interface, so it seemed unnecessary.

It cannot be detected at compile time:

class A
{
   int opCmp(Object o){...}
}

class B: A
{
   int opCmp(Object o)
   { throw new UnOrderedException("cannot order B");}
}

void f(A a, Object o)
{
   a < o; // can you tell at compile time whether a is a B or an A?
}

-Steve


April 18, 2008
On 18/04/2008, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> Yes.  That is a choice of the great-great-great-...-grandchild of A's author
>  :)

Hmmm.

I think more bugs are likely to occur as a result of people forgetting
to supply opCmp() and having the wrong opCmp() called.



>  By not overriding opEquals or opCmp, he is saying that if you compare it
>  with something else, A's implementation is the one to use.

...or he might have just forgotten, and it could be a bug.


>  If he wanted to
>  specify that opCmp should be different he would have rewritten opCmp.

...an /obscure/ bug, no less. One that might be very hard to track down.



>  >>  You need to have opEquals in Object in order to support hashing.
>
> > Please could you elaborate, as I don't understand why this is so?
>
> A hashtable works by <snip>

None of which implies that you need opEquals /in Object/. It only implies that you need it in any class that needs to do hashing. Again, this is a perfect case for explicit.



> It cannot be detected at compile time:
>
>  class A
>  {
>    int opCmp(Object o){...}
>  }
>
>  class B: A
>  {
>    int opCmp(Object o)
>    { throw new UnOrderedException("cannot order B");}
>  }
>
>  void f(A a, Object o)
>  {
>    a < o; // can you tell at compile time whether a is a B or an A?
>  }

No. On that point, you're right - although the code would look like thus under the new rules:

    class A
    {
        explicit int opCmp(explicit const(A) c) const {...}
    }

    class B : A
    {
        // no opCmp
    }

    void f(A a, A o)
    {
        if (a < o)...
    }

Hmmm. I guess it /would/ have to be a runtime exception after all. I stand corrected on that point. (You're also the second person to point out that error, so I guess there's a consensus there). OK, drop the "compile-time" claims! :-)
April 18, 2008
"Janice Caron" wrote
> On 18/04/2008, Steven Schveighoffer wrote:
>> Yes.  That is a choice of the great-great-great-...-grandchild of A's
>> author
>>  :)
>
> Hmmm.
>
> I think more bugs are likely to occur as a result of people forgetting
> to supply opCmp() and having the wrong opCmp() called.

I disagree.  Not overriding opCmp seems to me far more common than overriding it.  Many times one inherits from a class to change one bit of it, a bit that may not affect opCmp.  Should the compiler assume the user wants to override opCmp, and do some default action for him?  Or should you always have to alias the parent's opCmp?  I have a project, for instance, where I've made 10 classes.  None of those classes define opCmp, and the project works beautifully without it.

>>  By not overriding opEquals or opCmp, he is saying that if you compare it
>>  with something else, A's implementation is the one to use.
>
> ...or he might have just forgotten, and it could be a bug.

So what?  If he forgot to override f() from the base class that could be an error too.  There is no way for the compiler to prevent all bugs.

>>  >>  You need to have opEquals in Object in order to support hashing.
>>
>> > Please could you elaborate, as I don't understand why this is so?
>>
>> A hashtable works by <snip>
>
> None of which implies that you need opEquals /in Object/. It only implies that you need it in any class that needs to do hashing. Again, this is a perfect case for explicit.

It is not necessary in Object, but because it CAN be defined by default, why not?  This means any object can be a key without any extra code on the author's part.  The default opEquals might not be perfect, but it is consistent.  The default opCmp is not consistent.

-Steve


April 18, 2008
Steven Schveighoffer wrote:
> Huh?  I think maybe you meant C to inherit from B?  If so I see what you are trying to explain :)
>
> I don't really agree that opEquals or opCmp should not be inherited.  I
> think normal inheritance the way it is now is just fine.
> 
as Janice showed and I completely agree with her, this is a bad default. We are talking about the D default behavior here and it can be overridden. The programmer can define an opCmp that is not explicit and than all derived classes will inherit that opCmp as well, so your desired behavior is still available, it's just not the default anymore.
> You need to have opEquals in Object in order to support hashing.
> 
I'm not sure I fully understand you on this. the suggestion states that unless opEquals is defined, the compiler defaults to identity comparison.  This is equivalent to having an opEquals in Object that does the same thing (which is the current implementation, I think). I don't care if it's implemented inside the compiler, or via opEquals in Object, what's important is the semantics. i.e. unless there's a user defined opCmp for a class, (a == b) will be true iff (a is b) is true [ for two instances a and b of that class].
> The default opCmp should be redefined to throw an exception.  An alternative is that opCmp is defined in an interface.  Or you could do both.  The rationale is that opCmp doesn't have a default.  Some objects just cannot be expected to have a well-defined order.
>
> -Steve
>
>
> 
since D is relatively a new language, we can benefit by learning from
previous design errors in similar languages. Java implements that idea
of a method in Object that throw by default and that proved to be a very
fragile design. Object should provide only the methods that all objects
contain, and since not all classes are fully ordered, opCmp should not
be in Object.
I agree with you that providing a "Comparable" interface is a good
design. If D was only OOP like java, than this would have been my
favorite solution. indeed, Java contains such an interface and you are
expected to implement it if your class is comparable. if i remember
correctly, there are two interfaces in Java which facilitate the subtle
difference between an opEquals and opCmp comparison.
as Janice said, this interface is not strictly necessary with her
proposal, however such an interface can help document the fact that a
class is comparable, so it still could be added.

-- Yigal
April 18, 2008
On 18/04/2008, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>  >>  >>  You need to have opEquals in Object in order to support hashing.
>> > Please could you elaborate, as I don't understand why this is so?
> ...
>  It is not necessary in Object

Ah! That would explain why I didn't understand why it was so! :-)

I am confused about one point though. I know that one can implement an AA using hashing. I also know that one can implement an AA using comparison. However, it seems to me that one does not need /both/. How are D AA's implemented - do they use hashing, or do they use comparison? And why do the docs say we need both?
April 18, 2008
Janice Caron wrote:
> <snip>...But I do agree with you about needing a
> mechanism for such things. After all, we don't add a new keyword every
> time we add a function, so why should we need a new keyword for each
> new type-constructor or storage class? Someone else pointed out that
> the @ sign is unused in D, and could be used for annotations ... but
> that's an issue for another thread.
> 
OK, we seem to have reached a consensus and I agree that "explicit"
should  be optional as you said.
Robert said on a different thread that many good ideas and suggestions
get lost in the depth of the NG threads. since I don't know how to use
the bugzilla and don't even know where it is, may I ask you to please
add this suggestion to there as an enhancement request so it won't be
lost and maybe Walter would even agree to add it to D [fingers
crossed... :)].

regarding annotations/attributes: should we move this to a new thread and further discuss it, or maybe add it to bugzilla now as well?

-- Yigal
April 18, 2008
On 18/04/2008, Yigal Chripun <yigal100@gmail.com> wrote:
>  may I ask you to please
>  add this suggestion to there as an enhancement request

Not yet. I'm not sure there's a consensus at this stage. I tend to throw idea out and see how well they float, and there may be problems no one's thought of yet. Let's wait and see how it pans out. But yeah - if it pans out, I can do that eventually.


>  regarding annotations/attributes: should we move this to a new thread
>  and further discuss it

Dunno. Move it to another thread I guess. I'm not sure anyone's got a concrete idea of exactly how that would work yet.