View mode: basic / threaded / horizontal-split · Log in · Help
September 27, 2009
opEquals and the Non-Virtual Interface idiom
Here's an article about the perils of equals in Java (opEquals in D):

http://www.ddj.com/article/printableArticle.jhtml;jsessionid=GFKUCQH5S4IHNQE1GHOSKHWATMY32JVN?articleID=184405053&dept_url=/java/

It turns out this is a great example for NVI. In D, we could and should
do the following:

class Object {
    // Implement this
    private bool opEqualsImpl(Object rhs) {
        return false;
    }
    // Use this
    final bool opEquals(Object rhs) {
        if (this is rhs) return true;
        if (this is null || rhs is null) return false;
        return opEqualsImpl(rhs) && rhs.opEqualsImpl(this);
    }
}

I took advantage of the fact that in a final function this may be null
without an access violation. The implementation above ensures symmetry
of equality and has each class implement a simpler primitive.

What do you think?


Andrei
September 27, 2009
Re: opEquals and the Non-Virtual Interface idiom
Andrei Alexandrescu wrote:
> Here's an article about the perils of equals in Java (opEquals in D):
> 
> http://www.ddj.com/article/printableArticle.jhtml;jsessionid=GFKUCQH5S4IHNQE1GHOSKHWATMY32JVN?articleID=184405053&dept_url=/java/ 
> 
> 
> It turns out this is a great example for NVI. In D, we could and should
> do the following:
> 
> class Object {
>     // Implement this
>     private bool opEqualsImpl(Object rhs) {
>         return false;
>     }
>     // Use this
>     final bool opEquals(Object rhs) {
>         if (this is rhs) return true;
>         if (this is null || rhs is null) return false;
>         return opEqualsImpl(rhs) && rhs.opEqualsImpl(this);
>     }
> }
> 
> I took advantage of the fact that in a final function this may be null
> without an access violation. The implementation above ensures symmetry
> of equality and has each class implement a simpler primitive.
> 
> What do you think?

Eh, now after all this discussion, we're going to allow even "this" to 
be null? That seems like a backstep...

Implementing opEquals as a global/static function, that calls the actual 
Object.opEquals virtual method would be so much more straight forward.

PS: I agree about the NVI thing. If you'd go to extend the language for 
"NVI", couldn't we just introduce a second type of virtual function that 
works this way:
1. the super class' implementation is _always_ called first
2. the super class function can decide to "call down" to the sub class' 
implementation of the same method
=> no extra do<something> method needed, and the code is (possibly) clearer.

> Andrei
September 27, 2009
Re: opEquals and the Non-Virtual Interface idiom
grauzone wrote:
> Andrei Alexandrescu wrote:
>> Here's an article about the perils of equals in Java (opEquals in D):
>>
>> http://www.ddj.com/article/printableArticle.jhtml;jsessionid=GFKUCQH5S4IHNQE1GHOSKHWATMY32JVN?articleID=184405053&dept_url=/java/ 
>>
>>
>> It turns out this is a great example for NVI. In D, we could and should
>> do the following:
>>
>> class Object {
>>     // Implement this
>>     private bool opEqualsImpl(Object rhs) {
>>         return false;
>>     }
>>     // Use this
>>     final bool opEquals(Object rhs) {
>>         if (this is rhs) return true;
>>         if (this is null || rhs is null) return false;
>>         return opEqualsImpl(rhs) && rhs.opEqualsImpl(this);
>>     }
>> }
>>
>> I took advantage of the fact that in a final function this may be null
>> without an access violation. The implementation above ensures symmetry
>> of equality and has each class implement a simpler primitive.
>>
>> What do you think?
> 
> Eh, now after all this discussion, we're going to allow even "this" to 
> be null? That seems like a backstep...

Good point.

> Implementing opEquals as a global/static function, that calls the actual 
> Object.opEquals virtual method would be so much more straight forward.

It's also less safe because people could call the incomplete primitive 
by hand. With NVI in place nobody outside object.d could ever call 
opEqualsImpl.

> PS: I agree about the NVI thing. If you'd go to extend the language for 
> "NVI", couldn't we just introduce a second type of virtual function that 
> works this way:
> 1. the super class' implementation is _always_ called first
> 2. the super class function can decide to "call down" to the sub class' 
> implementation of the same method
> => no extra do<something> method needed, and the code is (possibly) 
> clearer.

Do you know of a precedent for this? One thing that NVI has going for it 
is that it's quite established - it seems to be solidly planted in 
programmers' lore. I was surprised to find 2.8M Google matches for the 
exact string "Non-Virtual Interface".


Andrei
September 27, 2009
Re: opEquals and the Non-Virtual Interface idiom
grauzone wrote:
> Andrei Alexandrescu wrote:
>> Here's an article about the perils of equals in Java (opEquals in D):
>>
>> http://www.ddj.com/article/printableArticle.jhtml;jsessionid=GFKUCQH5S4IHNQE1GHOSKHWATMY32JVN?articleID=184405053&dept_url=/java/ 
>>
>>
>> It turns out this is a great example for NVI. In D, we could and should
>> do the following:
>>
>> class Object {
>>     // Implement this
>>     private bool opEqualsImpl(Object rhs) {
>>         return false;
>>     }
>>     // Use this
>>     final bool opEquals(Object rhs) {
>>         if (this is rhs) return true;
>>         if (this is null || rhs is null) return false;
>>         return opEqualsImpl(rhs) && rhs.opEqualsImpl(this);
>>     }
>> }
>>
>> I took advantage of the fact that in a final function this may be null
>> without an access violation. The implementation above ensures symmetry
>> of equality and has each class implement a simpler primitive.
>>
>> What do you think?
> 
> Eh, now after all this discussion, we're going to allow even "this" to 
> be null? That seems like a backstep...

How is it a backstep? It's perfectly valid behavior.

Object foo;
foo.opEquals(foo);

The call itself will *always* succeed, its when 'this' gets referenced 
in opEquals that the code will crash.

> Implementing opEquals as a global/static function, that calls the actual 
> Object.opEquals virtual method would be so much more straight forward.

I agree, I prefer methods to end with Impl to stay hidden instead of 
being the ones to override.

> PS: I agree about the NVI thing. If you'd go to extend the language for 
> "NVI", couldn't we just introduce a second type of virtual function that 
> works this way:
> 1. the super class' implementation is _always_ called first
> 2. the super class function can decide to "call down" to the sub class' 
> implementation of the same method
> => no extra do<something> method needed, and the code is (possibly) 
> clearer.
> 
>> Andrei

I don't know how useful that would be, when you override a method you 
usually want to call the super method somewhere in the new 
implementation, not always at the beginning.
September 27, 2009
Re: opEquals and the Non-Virtual Interface idiom
On 27/09/2009 19:01, Andrei Alexandrescu wrote:
> grauzone wrote:
>> Andrei Alexandrescu wrote:
>>> Here's an article about the perils of equals in Java (opEquals in D):
>>>
>>> http://www.ddj.com/article/printableArticle.jhtml;jsessionid=GFKUCQH5S4IHNQE1GHOSKHWATMY32JVN?articleID=184405053&dept_url=/java/
>>>
>>>
>>> It turns out this is a great example for NVI. In D, we could and should
>>> do the following:
>>>
>>> class Object {
>>> // Implement this
>>> private bool opEqualsImpl(Object rhs) {
>>> return false;
>>> }
>>> // Use this
>>> final bool opEquals(Object rhs) {
>>> if (this is rhs) return true;
>>> if (this is null || rhs is null) return false;
>>> return opEqualsImpl(rhs) && rhs.opEqualsImpl(this);
>>> }
>>> }
>>>
>>> I took advantage of the fact that in a final function this may be null
>>> without an access violation. The implementation above ensures symmetry
>>> of equality and has each class implement a simpler primitive.
>>>
>>> What do you think?
>>
>> Eh, now after all this discussion, we're going to allow even "this" to
>> be null? That seems like a backstep...
>
> Good point.
>
>> Implementing opEquals as a global/static function, that calls the
>> actual Object.opEquals virtual method would be so much more straight
>> forward.
>
> It's also less safe because people could call the incomplete primitive
> by hand. With NVI in place nobody outside object.d could ever call
> opEqualsImpl.
>
>> PS: I agree about the NVI thing. If you'd go to extend the language
>> for "NVI", couldn't we just introduce a second type of virtual
>> function that works this way:
>> 1. the super class' implementation is _always_ called first
>> 2. the super class function can decide to "call down" to the sub
>> class' implementation of the same method
>> => no extra do<something> method needed, and the code is (possibly)
>> clearer.
>
> Do you know of a precedent for this? One thing that NVI has going for it
> is that it's quite established - it seems to be solidly planted in
> programmers' lore. I was surprised to find 2.8M Google matches for the
> exact string "Non-Virtual Interface".
>
>
> Andrei
>

This is a smalltalk idea -

method:
  ^ self method

by sending a message to self without implementing it you make the method 
abstract.
of course you can add pre/post code.

I also seen this with the keyword inner:

class Foo {
  void bar() {
      ... setup
      inner();
      ...tear down
  }
}
September 27, 2009
Re: opEquals and the Non-Virtual Interface idiom
Jeremie Pelletier wrote:
> grauzone wrote:
>> Andrei Alexandrescu wrote:
>>> Here's an article about the perils of equals in Java (opEquals in D):
>>>
>>> http://www.ddj.com/article/printableArticle.jhtml;jsessionid=GFKUCQH5S4IHNQE1GHOSKHWATMY32JVN?articleID=184405053&dept_url=/java/
>>>
>>>
>>> It turns out this is a great example for NVI. In D, we could and should
>>> do the following:
>>>
>>> class Object {
>>>     // Implement this
>>>     private bool opEqualsImpl(Object rhs) {
>>>         return false;
>>>     }
>>>     // Use this
>>>     final bool opEquals(Object rhs) {
>>>         if (this is rhs) return true;
>>>         if (this is null || rhs is null) return false;
>>>         return opEqualsImpl(rhs) && rhs.opEqualsImpl(this);
>>>     }
>>> }
>>>
>>> I took advantage of the fact that in a final function this may be null
>>> without an access violation. The implementation above ensures symmetry
>>> of equality and has each class implement a simpler primitive.
>>>
>>> What do you think?
>>
>> Eh, now after all this discussion, we're going to allow even "this" to
>> be null? That seems like a backstep...
> 
> How is it a backstep? It's perfectly valid behavior.
> 
> Object foo;
> foo.opEquals(foo);
> 

What is the semantics of that call?

"Is something undefined equal to something undefined"? I'm not sure why that should be considered valid behavior, given that it's unanswerable.

> The call itself will *always* succeed, its when 'this' gets referenced
> in opEquals that the code will crash.

Or when a contract is run. Or when an inherited contract is run. Or when the method attempts to lock the synchronization object.

> 
>> Implementing opEquals as a global/static function, that calls the
>> actual Object.opEquals virtual method would be so much more straight
>> forward.
> 
> I agree, I prefer methods to end with Impl to stay hidden instead of
> being the ones to override.
> 
>> PS: I agree about the NVI thing. If you'd go to extend the language
>> for "NVI", couldn't we just introduce a second type of virtual
>> function that works this way:
>> 1. the super class' implementation is _always_ called first
>> 2. the super class function can decide to "call down" to the sub
>> class' implementation of the same method
>> => no extra do<something> method needed, and the code is (possibly)
>> clearer.
>>
>>> Andrei
> 
> I don't know how useful that would be, when you override a method you
> usually want to call the super method somewhere in the new
> implementation, not always at the beginning.
September 27, 2009
Re: opEquals and the Non-Virtual Interface idiom
Andrei Alexandrescu wrote:
> Do you know of a precedent for this? One thing that NVI has going for it 
> is that it's quite established - it seems to be solidly planted in 
> programmers' lore. I was surprised to find 2.8M Google matches for the 
> exact string "Non-Virtual Interface".

Looks like it's considered a design pattern, and design patterns are 
popular. I think the idea itself is relatively obvious and useful, so 
it's likely to encounter it often. (I didn't even know it's called "NVI".)

By the way, Aspect Oriented Programming also seems to reinvent this idea 
with pre/post code for methods (see Yigal's posting).
September 28, 2009
Re: opEquals and the Non-Virtual Interface idiom
Andrei Alexandrescu Wrote:

> It turns out this is a great example for NVI. In D, we could and should
> do the following:
> 
> class Object {
>      // Implement this
>      private bool opEqualsImpl(Object rhs) {
>          return false;
>      }
>      // Use this
>      final bool opEquals(Object rhs) {
>          if (this is rhs) return true;
>          if (this is null || rhs is null) return false;
>          return opEqualsImpl(rhs) && rhs.opEqualsImpl(this);
>      }
> }

"I took advantage of the fact that in a final function this may be null without an access violation."

That "advantage" cannot be the general case?
Surely that statement is only true when the final function is in a base class, X, (and X can only be Object in D, right?)
for reason that the compiler can spot that the method is never overriden in ANY subclass of X (Object) , and therefore the method can be called directly rather than having to be dispatched through a VFT and consequently there is no VFT entry for that method/function.

Coming from C++ reasoning (maybe rules are subtlety different in D),
the thought experiment goes like this ...

class A {
 bool opEquals(Object rhs) {print( "Hello A"); return false;}
}

class B: A {
 final bool opEquals(Object rhs) {print( "Hello B"); return false;}
}

class C: A {
}

B b = new B();
b.opEquals(obj);

prints: Hello B

C c = new C();
c.opEquals(obj);

prints: Hello A

C c;
c.opEquals(obj);

prints: An error has occurred in this application.
Please contact the program vendor for an upgrade.
Send error report to Microsoft?
Yes? No (crashing out like this is an advertised feature of the language this program was developed in)?

Just conjecture.

-- Justin Johansson
September 28, 2009
Re: opEquals and the Non-Virtual Interface idiom
Justin Johansson wrote:
> Andrei Alexandrescu Wrote:
> 
>> It turns out this is a great example for NVI. In D, we could and should
>> do the following:
>>
>> class Object {
>>      // Implement this
>>      private bool opEqualsImpl(Object rhs) {
>>          return false;
>>      }
>>      // Use this
>>      final bool opEquals(Object rhs) {
>>          if (this is rhs) return true;
>>          if (this is null || rhs is null) return false;
>>          return opEqualsImpl(rhs) && rhs.opEqualsImpl(this);
>>      }
>> }
> 
> "I took advantage of the fact that in a final function this may be null without an access violation."
> 
> That "advantage" cannot be the general case?
> Surely that statement is only true when the final function is in a base class, X, (and X can only be Object in D, right?)
> for reason that the compiler can spot that the method is never overriden in ANY subclass of X (Object) , and therefore the method can be called directly rather than having to be dispatched through a VFT and consequently there is no VFT entry for that method/function.

Sorry, indeed I meant a "introducing final" function, not a final 
function. A final function that overrides one in the base class must 
often go through the vtable. Though if a final function (introducing or 
not) gets called for the static type that made it final, it needn't go 
through the vtable so a null this could be allowed inside of it.


Andrei
Top | Discussion index | About this forum | D home