Bug 2050 – interfaces should allow final methods with body
Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
x86
OS
Linux
Creation time
2008-04-28T01:55:00Z
Last change time
2015-06-09T01:31:19Z
Assigned to
nobody
Creator
andrei
Comments
Comment #0 by andrei — 2008-04-28T01:55:43Z
Consider:
interface Foo
{
void bar();
final void baz() { bar; bar; }
}
This code doesn't go through on grounds that interfaces cannot define methods with bodies. In fact, final methods don't break any laws of nature because they are not meant to be overriden and, aside from lookup, have the regime of ordinary functions.
It would appear that this issue is easy to mitigate:
interface Foo
{
void bar();
}
void baz(Foo foo) { foo.bar; foo.bar; }
Generally I favor free functions so I'd be happy with the above. But there are two troublesome scenarios:
1. Operators must be members
2. Sutter's NVI idiom (http://www.gotw.ca/publications/mill18.htm) can be implemented more clearly with members than with virtual functions. There are a couple of other bugs applying to NVI in D as well, that I'll post later.
Comment #1 by wbaxter — 2008-04-28T02:07:39Z
That would open the door to the dreaded diamond pattern.
You'd just have the compiler generate ambiguity errors in those cases I suppose?
Comment #2 by andrei — 2008-04-28T02:21:14Z
(In reply to comment #1)
> That would open the door to the dreaded diamond pattern.
>
> You'd just have the compiler generate ambiguity errors in those cases I
> suppose?
The diamond pattern can't occur in schemes with single inheritance of implementation.
Comment #3 by aarti — 2008-04-28T03:56:56Z
It seems that same rationale applies to static functions in interfaces. Currently below code doesn't work, as static functions can not have body in interface.
---
int a;
interface A {
static int number() {
return a;
}
}
---
Current situation is just plainly wrong as it is possible to put static functions into interfaces and it is not possible to use them at all - linker error is emitted during compilation (what is understandable as static functions are not virtual).
Comment #4 by fraserofthenight — 2008-04-28T05:10:45Z
[email protected] wrote:
> http://d.puremagic.com/issues/show_bug.cgi?id=2050
>
>
> [email protected] changed:
>
> What |Removed |Added
> ----------------------------------------------------------------------------
> CC| |[email protected]
>
>
>
>
> ------- Comment #3 from [email protected] 2008-04-28 03:56 -------
> It seems that same rationale applies to static functions in interfaces.
> Currently below code doesn't work, as static functions can not have body in
> interface.
>
> ---
> int a;
> interface A {
> static int number() {
> return a;
> }
> }
> ---
>
> Current situation is just plainly wrong as it is possible to put static
> functions into interfaces and it is not possible to use them at all - linker
> error is emitted during compilation (what is understandable as static functions
> are not virtual).
You should be able to call them on the interface: If you have:
class B : A {}
void main()
{
A a = new B();
B b = new B();
A.number(); // Should work fine, so here's the bug
a.number(); // Maybe should work....?
b.number(); // Should NOT work
}
Comment #5 by fraserofthenight — 2008-04-28T05:10:52Z
[email protected] wrote:
> http://d.puremagic.com/issues/show_bug.cgi?id=2050
>
>
>
>
>
> ------- Comment #2 from [email protected] 2008-04-28 02:21 -------
> (In reply to comment #1)
>> That would open the door to the dreaded diamond pattern.
>>
>> You'd just have the compiler generate ambiguity errors in those cases I
>> suppose?
>
> The diamond pattern can't occur in schemes with single inheritance of
> implementation.
That's true, but the purpose of interfaces is to allow for multiple
inheritance in a safe way. Say you have two interfaces, IZombie from
Library A and IPirate from Library B. You have a class ZombiePirate that
implements both these interfaces. Right now, IZombie has a final method
"dance()", and IPirate has the dance() method as virtual. All is well,
since a call to an instance's dance() method would result in
IZombie.dance()'s implementation being called. But if IPirate suddenly
got a dance() implementation, your code would break simply by updating
Library B.
Comment #6 by andrei — 2008-04-28T11:12:34Z
(In reply to comment #5)
> [email protected] wrote:
> > http://d.puremagic.com/issues/show_bug.cgi?id=2050
> >
> >
> >
> >
> >
> > ------- Comment #2 from [email protected] 2008-04-28 02:21 -------
> > (In reply to comment #1)
> >> That would open the door to the dreaded diamond pattern.
> >>
> >> You'd just have the compiler generate ambiguity errors in those cases I
> >> suppose?
> >
> > The diamond pattern can't occur in schemes with single inheritance of
> > implementation.
>
> That's true, but the purpose of interfaces is to allow for multiple
> inheritance in a safe way. Say you have two interfaces, IZombie from
> Library A and IPirate from Library B. You have a class ZombiePirate that
> implements both these interfaces. Right now, IZombie has a final method
> "dance()", and IPirate has the dance() method as virtual. All is well,
> since a call to an instance's dance() method would result in
> IZombie.dance()'s implementation being called. But if IPirate suddenly
> got a dance() implementation, your code would break simply by updating
> Library B.
Such cases would be flagged as ambiguous.
Comment #7 by andrei — 2008-04-28T11:14:04Z
(In reply to comment #3)
> It seems that same rationale applies to static functions in interfaces.
I agree.
Comment #8 by fraserofthenight — 2008-04-28T11:21:18Z
[email protected] wrote:
> http://d.puremagic.com/issues/show_bug.cgi?id=2050
>
>
>
>
>
> ------- Comment #6 from [email protected] 2008-04-28 11:12 -------
> (In reply to comment #5)
>> [email protected] wrote:
>>> http://d.puremagic.com/issues/show_bug.cgi?id=2050
>>>
>>>
>>>
>>>
>>>
>>> ------- Comment #2 from [email protected] 2008-04-28 02:21 -------
>>> (In reply to comment #1)
>>>> That would open the door to the dreaded diamond pattern.
>>>>
>>>> You'd just have the compiler generate ambiguity errors in those cases I
>>>> suppose?
>>> The diamond pattern can't occur in schemes with single inheritance of
>>> implementation.
>> That's true, but the purpose of interfaces is to allow for multiple
>> inheritance in a safe way. Say you have two interfaces, IZombie from
>> Library A and IPirate from Library B. You have a class ZombiePirate that
>> implements both these interfaces. Right now, IZombie has a final method
>> "dance()", and IPirate has the dance() method as virtual. All is well,
>> since a call to an instance's dance() method would result in
>> IZombie.dance()'s implementation being called. But if IPirate suddenly
>> got a dance() implementation, your code would break simply by updating
>> Library B.
>
> Such cases would be flagged as ambiguous.
Right, but the ambiguity would be caused at no fault of the code being
compiled, but because something changed in a different module. In many
ways, it's similar to hijacking.
Comment #9 by sandford — 2009-04-04T09:53:08Z
(In reply to comment #0)
This seems to break composability:
interface I1 { final int foo() {return 1;} }
interface I2 { final int foo() {return 2;} }
class A: I1, I2 {}
Whose foo does A choose? And how is it resolved? (given you can't drop one of the interfaces). And consider
class B: I1, I2 { final int foo() {return 3;} }
B b = new B();
I1 i1 = b;
I2 i2 = b;
assert( i1.foo == 1 );
assert( i2.foo == 2 );
assert( b.foo == 3 );
Comment #10 by sandford — 2009-04-04T10:01:48Z
(In reply to comment #0)
> Consider:
>
> interface Foo
> {
> void bar();
> final void baz() { bar; bar; }
> }
How is this superior to the current interface + mixin approach? Or an mixin-able interface approach?:
class A:Foo {
mixin Foo; // Add Foo's final/static method bodies to A
}
Comment #11 by andrei — 2009-04-04T10:58:15Z
(In reply to comment #9)
> (In reply to comment #0)
> This seems to break composability:
>
> interface I1 { final int foo() {return 1;} }
> interface I2 { final int foo() {return 2;} }
>
> class A: I1, I2 {}
>
> Whose foo does A choose? And how is it resolved? (given you can't drop one of
> the interfaces).
I think that's an ambiguity. It might be busted by having A write e.g. alias I1.foo foo;
> And consider
>
> class B: I1, I2 { final int foo() {return 3;} }
> B b = new B();
> I1 i1 = b;
> I2 i2 = b;
> assert( i1.foo == 1 );
> assert( i2.foo == 2 );
> assert( b.foo == 3 );
A good use of final functions is to enable the non-virtual interface idiom. The point of that idiom is partially that you cannot override final functions in interfaces.
Comment #12 by sandford — 2010-07-07T07:23:10Z
Wasn't this added in D 2.040. (i.e. should this be closed?)