www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - When does final mean "maybe, kinda, sorta" ?

reply kris <foo bar.com> writes:
"That's a great deal to make one word mean," Alice said in a thoughtful 
tone. "When I make a word do a lot of work like that," said Humpty 
Dumpty, "I always pay it extra."


Reading one of Andrei's posts about const and final reminded me of what 
I consider to be a D wart. This is taken directly from the D reference 
manual:

"Functions marked as final may not be overridden in a derived class, 
unless they are also private."

Just how much does "final" get paid for this? Onto the given example:


<quote>

class A
{
     int def() { ... }
     final int foo() { ... }
     final private int bar() { ... }
     private int abc() { ... }
}

class B : A
{
     int def() { ... }	// ok, overrides A.def
     int foo() { ... }	// error, A.foo is final
     int bar() { ... }	// ok, A.bar is final private, but not virtual
     int abc() { ... }	// ok, A.abc is not virtual, B.abc is virtual
}

void test(A a)
{
     a.def();	// calls B.def
     a.foo();	// calls A.foo
     a.bar();	// calls A.bar
     a.abc();	// calls A.abc
}

void func()
{   B b = new B();
     test(b);
}

</quote>


1) You had to read that at least twice, didn't you? :)

2) Seems to me the usage of "final" here is entirely misleading, and 
opens up a lurking hole for some poor sod to break their ankle in.

3) the example (from the ref manual) highlights overriding of private 
and private-final methods. Surely this applies only within module scope? 
That is, within the same module? Assuming that's the case, why would any 
sane engineer do such a thing instead of changing the original class 
code? They have the source, obviously, and overriding private seems 
quite dubious at best. I can think of one case, but can't see why the 
language would need to support it explicitly.

In short, it appears the example exhibits some poor practice (overriding 
private methods), is somewhat misleading (one certainly *hopes* private 
cannot be seen beyond module-scope), and discusses how to make "final" 
mean something quite other than final.

How about fixing final? Or name it something more appropriate? It really 
shouldn't get paid any more than other keywords ...

- Kris
Jan 22 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
kris wrote:
[snip]
 In short, it appears the example exhibits some poor practice (overriding 
 private methods), is somewhat misleading (one certainly *hopes* private 
 cannot be seen beyond module-scope), and discusses how to make "final" 
 mean something quite other than final.

I agree about the misleading part, but I don't think that overriding private methods is poor practice. In fact I think it's rich practice :o). See e.g. http://www.gotw.ca/publications/mill18.htm. Andrei
Jan 22 2007
next sibling parent kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 [snip]
 
 In short, it appears the example exhibits some poor practice 
 (overriding private methods), is somewhat misleading (one certainly 
 *hopes* private cannot be seen beyond module-scope), and discusses how 
 to make "final" mean something quite other than final.

I agree about the misleading part, but I don't think that overriding private methods is poor practice. In fact I think it's rich practice :o). See e.g. http://www.gotw.ca/publications/mill18.htm. Andrei

Good point. With D, however, the override itself would probably have to be within the same module as the Template Method?
Jan 22 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 [snip]
 In short, it appears the example exhibits some poor practice 
 (overriding private methods), is somewhat misleading (one certainly 
 *hopes* private cannot be seen beyond module-scope), and discusses how 
 to make "final" mean something quite other than final.

I agree about the misleading part, but I don't think that overriding private methods is poor practice. In fact I think it's rich practice :o). See e.g. http://www.gotw.ca/publications/mill18.htm.

It's a good design approach, but there's no reason the virtual methods must be private--they could be protected as well. One could argue that the design is cleaner with the virtual methods private, but since the methods must be overridden by the derived class it's not like any protection attributes are being maintained anyway. Isn't this a current topic of discussion on comp.l.c++.m? :-) Sean
Jan 22 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 [snip]
 In short, it appears the example exhibits some poor practice 
 (overriding private methods), is somewhat misleading (one certainly 
 *hopes* private cannot be seen beyond module-scope), and discusses 
 how to make "final" mean something quite other than final.

I agree about the misleading part, but I don't think that overriding private methods is poor practice. In fact I think it's rich practice :o). See e.g. http://www.gotw.ca/publications/mill18.htm.

It's a good design approach, but there's no reason the virtual methods must be private--they could be protected as well. One could argue that the design is cleaner with the virtual methods private, but since the methods must be overridden by the derived class it's not like any protection attributes are being maintained anyway. Isn't this a current topic of discussion on comp.l.c++.m? :-)

There is a big difference between private and protected. Private means that later-added code cannot call the method, period. It could be said that private is "distantly more private" than both protected and public because both of the latter allow access by unbounded amounts of code. So there is indeed something interesting by being required to implement something that you're not allowed to call. Something like Shirley Temple acting in movies she was too young to be allowed to watch :o). Andrei
Jan 22 2007
parent reply Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 [snip]
 In short, it appears the example exhibits some poor practice 
 (overriding private methods), is somewhat misleading (one certainly 
 *hopes* private cannot be seen beyond module-scope), and discusses 
 how to make "final" mean something quite other than final.

I agree about the misleading part, but I don't think that overriding private methods is poor practice. In fact I think it's rich practice :o). See e.g. http://www.gotw.ca/publications/mill18.htm.

It's a good design approach, but there's no reason the virtual methods must be private--they could be protected as well. One could argue that the design is cleaner with the virtual methods private, but since the methods must be overridden by the derived class it's not like any protection attributes are being maintained anyway. Isn't this a current topic of discussion on comp.l.c++.m? :-)

There is a big difference between private and protected. Private means that later-added code cannot call the method, period. It could be said that private is "distantly more private" than both protected and public because both of the latter allow access by unbounded amounts of code. So there is indeed something interesting by being required to implement something that you're not allowed to call. Something like Shirley Temple acting in movies she was too young to be allowed to watch :o).

But you can call it, or even change its visibility: class Base { private: virtual void fn() = 0; }; class Derived : Base { public: virtual void fn() {} void call() { fn(); } }; Sure, you can't call it through Base::fn(), but... :-) I'll admit it's a clever design pattern, and one I use myself in C++, but I think there is also value in privates being implicitly final as in D. No expressive power is really lost--the methods could be made protected just as easily--and I think it actually makes more conceptual sense that private functions cannot be overridden. Sean
Jan 22 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 [snip]
 In short, it appears the example exhibits some poor practice 
 (overriding private methods), is somewhat misleading (one certainly 
 *hopes* private cannot be seen beyond module-scope), and discusses 
 how to make "final" mean something quite other than final.

I agree about the misleading part, but I don't think that overriding private methods is poor practice. In fact I think it's rich practice :o). See e.g. http://www.gotw.ca/publications/mill18.htm.

It's a good design approach, but there's no reason the virtual methods must be private--they could be protected as well. One could argue that the design is cleaner with the virtual methods private, but since the methods must be overridden by the derived class it's not like any protection attributes are being maintained anyway. Isn't this a current topic of discussion on comp.l.c++.m? :-)

There is a big difference between private and protected. Private means that later-added code cannot call the method, period. It could be said that private is "distantly more private" than both protected and public because both of the latter allow access by unbounded amounts of code. So there is indeed something interesting by being required to implement something that you're not allowed to call. Something like Shirley Temple acting in movies she was too young to be allowed to watch :o).

But you can call it, or even change its visibility: class Base { private: virtual void fn() = 0; }; class Derived : Base { public: virtual void fn() {} void call() { fn(); } }; Sure, you can't call it through Base::fn(), but... :-)

Oh, my hope was that D disallows republicizing private functions :o(. Spectacular failure to provide a great feature... Andrei
Jan 23 2007
parent reply Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 [snip]
 In short, it appears the example exhibits some poor practice 
 (overriding private methods), is somewhat misleading (one 
 certainly *hopes* private cannot be seen beyond module-scope), and 
 discusses how to make "final" mean something quite other than final.

I agree about the misleading part, but I don't think that overriding private methods is poor practice. In fact I think it's rich practice :o). See e.g. http://www.gotw.ca/publications/mill18.htm.

It's a good design approach, but there's no reason the virtual methods must be private--they could be protected as well. One could argue that the design is cleaner with the virtual methods private, but since the methods must be overridden by the derived class it's not like any protection attributes are being maintained anyway. Isn't this a current topic of discussion on comp.l.c++.m? :-)

There is a big difference between private and protected. Private means that later-added code cannot call the method, period. It could be said that private is "distantly more private" than both protected and public because both of the latter allow access by unbounded amounts of code. So there is indeed something interesting by being required to implement something that you're not allowed to call. Something like Shirley Temple acting in movies she was too young to be allowed to watch :o).

But you can call it, or even change its visibility: class Base { private: virtual void fn() = 0; }; class Derived : Base { public: virtual void fn() {} void call() { fn(); } }; Sure, you can't call it through Base::fn(), but... :-)

Oh, my hope was that D disallows republicizing private functions :o(. Spectacular failure to provide a great feature...

I think it effectively does, since private functions can not be overridden. The above was C++ (I should have labeled the code). Sean
Jan 23 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 class Base
 {
 private:
     virtual void fn() = 0;
 };

 class Derived : Base
 {
 public:
      virtual void fn() {}
     void call() { fn(); }
 };

 Sure, you can't call it through Base::fn(), but... :-)

Oh, my hope was that D disallows republicizing private functions :o(. Spectacular failure to provide a great feature...

I think it effectively does, since private functions can not be overridden. The above was C++ (I should have labeled the code).

That's great news. The semicolons should have informed me... oh, and wait, there's no "virtual" in D... :o) Andrei
Jan 23 2007
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 class Base
 {
 private:
     virtual void fn() = 0;
 };

 class Derived : Base
 {
 public:
      virtual void fn() {}
     void call() { fn(); }
 };

 Sure, you can't call it through Base::fn(), but... :-)

Oh, my hope was that D disallows republicizing private functions :o(. Spectacular failure to provide a great feature...

I think it effectively does, since private functions can not be overridden. The above was C++ (I should have labeled the code).

That's great news. The semicolons should have informed me... oh, and wait, there's no "virtual" in D... :o)

And there's definitely no '= 0' at the end of method declarations. Oh, and his comment at the end mentioned 'Base::fn()' instead of 'Base.fn()' :)
Jan 23 2007
prev sibling next sibling parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 [snip]
 I agree about the misleading part, but I don't think that overriding 
 private methods is poor practice. In fact I think it's rich practice 
 :o). See e.g. http://www.gotw.ca/publications/mill18.htm.
 
 Andrei

Design philosophy aside, what should be done about the issues with "final" itself?
Jan 22 2007
parent reply Sean Kelly <sean f4.ca> writes:
kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 [snip]
 I agree about the misleading part, but I don't think that overriding 
 private methods is poor practice. In fact I think it's rich practice 
 :o). See e.g. http://www.gotw.ca/publications/mill18.htm.

 Andrei

Design philosophy aside, what should be done about the issues with "final" itself?

I think the current design is consistent but potentially confusing. If a function can be virtual (ie. if it is not private) then "final" controls whether it can be overridden. So making a private function final is meaningless because it cannot be overridden--in this case, "final" is simply ignored. Sean
Jan 23 2007
parent reply kris <foo bar.com> writes:
Sean Kelly wrote:
 kris wrote:
 
 Andrei Alexandrescu (See Website For Email) wrote:

 kris wrote:
 [snip]
 I agree about the misleading part, but I don't think that overriding 
 private methods is poor practice. In fact I think it's rich practice 
 :o). See e.g. http://www.gotw.ca/publications/mill18.htm.

 Andrei

Design philosophy aside, what should be done about the issues with "final" itself?

I think the current design is consistent but potentially confusing. If a function can be virtual (ie. if it is not private) then "final" controls whether it can be overridden. So making a private function final is meaningless because it cannot be overridden--in this case, "final" is simply ignored. Sean

Fair enough. Then the compiler should be a tad smarter about disallowing 'stupid' combinations of attributes. This applies to many other attributes, not just final. People /will/ use whatever combination they're allowed to, and the doc appears to encourage them to do so.
Jan 23 2007
parent Sean Kelly <sean f4.ca> writes:
kris wrote:
 Sean Kelly wrote:
 kris wrote:

 Andrei Alexandrescu (See Website For Email) wrote:

 kris wrote:
 [snip]
 I agree about the misleading part, but I don't think that overriding 
 private methods is poor practice. In fact I think it's rich practice 
 :o). See e.g. http://www.gotw.ca/publications/mill18.htm.

 Andrei

Design philosophy aside, what should be done about the issues with "final" itself?

I think the current design is consistent but potentially confusing. If a function can be virtual (ie. if it is not private) then "final" controls whether it can be overridden. So making a private function final is meaningless because it cannot be overridden--in this case, "final" is simply ignored.

Fair enough. Then the compiler should be a tad smarter about disallowing 'stupid' combinations of attributes. This applies to many other attributes, not just final. People /will/ use whatever combination they're allowed to, and the doc appears to encourage them to do so.

The spec does this in a number of places. For example: const auto static auto const auto i = 5; // what is i?
Jan 23 2007
prev sibling parent John Reimer <terminal.node gmail.com> writes:
On Tue, 23 Jan 2007 12:08:39 +0100, Frits van Bommel wrote:

 
 And there's definitely no '= 0' at the end of method declarations. Oh, 
 and his comment at the end mentioned 'Base::fn()' instead of 'Base.fn()' :)

Yeah! Rub it in... lol! :D -JJR
Jan 23 2007
prev sibling parent reply Steve Horne <stephenwantshornenospam100 aol.com> writes:
On Mon, 22 Jan 2007 15:31:25 -0800, kris <foo bar.com> wrote:

"Functions marked as final may not be overridden in a derived class, 
unless they are also private."

Just how much does "final" get paid for this? Onto the given example:

...
2) Seems to me the usage of "final" here is entirely misleading, and 
opens up a lurking hole for some poor sod to break their ankle in.

Having been in C++ mode for a bit, I'm probably misunderstanding the issue, but for what it's worth... Aren't private members excluded from the virtual table? Early bound, in other words? If so, one way to view it is that private methods cannot be overridden and are therefore inherently final, so it does no particular harm to actually say that they are final. Either requiring 'final' or banning it might help code consistency and readability, but it seems like a minor issue. How can I say that a private method can't be overridden? Well, if there is no late binding, a new method with the same signature isn't really an override. The private method doesn't really exist in any derived classes interface, internal or external. The only reason that derived classes are aware at all is to improve the compiler error messages. So any method added to a derived class with the same signature is just a completely new and independent method - not an override. It's really no different to having a method with the same signature in an unrelated class. Potentially confusing enough to get a note in the documentation, maybe, but do we really want to ban derived classes from using certain method signatures just because they were used for private methods in the base class? That sounds like a violation of encapsulation to me. The derived classes shouldn't need to know about the private members of the base class at all, even for the purposes of avoiding name clashes. And adding a new private member to a base class should not break any derived classes that might happen to already use the same signature for a (private or public) method, especially since you may have no control or even knowledge of those derived classes. Ah! you say, but we're talking about code in the same module. But then we get different rules for whether a derived class can define certain method signatures based on which module it happens to be in. Sure, you can rename the private methods in the base class to avoid a clash if you need to, but without reliable refactoring tools to catch all the calls, that can be potentially error-prone. -- Remove 'wants' and 'nospam' from e-mail.
Jan 22 2007
parent reply kris <foo bar.com> writes:
Steve Horne wrote:
[snip]

 And adding a new private member to a base class should not
 break any derived classes that might happen to already use the same
 signature for a (private or public) method, especially since you may
 have no control or even knowledge of those derived classes.

Given that, I'd imagine you'd be a proponent of making 'override' a required keyword? After all, not requiring it will likely lead to the same type of problem when a base-class method is removed. Yet, judging by past record, override is unlikely to ever be required.
Jan 23 2007
parent reply Steve Horne <stephenwantshornenospam100 aol.com> writes:
On Tue, 23 Jan 2007 00:26:10 -0800, kris <foo bar.com> wrote:

Steve Horne wrote:
[snip]

 And adding a new private member to a base class should not
 break any derived classes that might happen to already use the same
 signature for a (private or public) method, especially since you may
 have no control or even knowledge of those derived classes.

Given that, I'd imagine you'd be a proponent of making 'override' a required keyword? After all, not requiring it will likely lead to the same type of problem when a base-class method is removed. Yet, judging by past record, override is unlikely to ever be required.

Override should only be specified when the intent is to override, IMO. If adding a new public/protected method to a base class results in its accidentally being overridden by a pre-existing conflicting derived-class method, clearly that's bad. So... 1. Specifying override on a method that doesn't override any base class method should be an error. 2. Failing to specify override on a method that does override a base class public/protected method should be an error. I thought that was what D does anyway, and for that matter that it was the whole point of the override keyword - but, as I said, I'm more in C++ mode ATM (and haven't been using D that long anyway). Presumably removal of an overridden base-class method would be covered by case 1, which is why you are saying it should be required, and therefore I assume this case 1 error doesn't occur. Which gets a reading on my odd-ometer. Mind you, removing a public/protected interface from a published class should be a pretty unusual thing to do. Getting people dependent on an interface and then just taking it away without warning is more than a bit dodgy. The possibility of problems shouldn't really come as a surprise. It's similar to the rule for COM interfaces - extending is OK, different implementations OK, but compatibility with previously published interfaces should be maintained (though the main breaker of that rule seems to be Microsoft itself). When adding new methods creates problems, there is genuine reason why you wouldn't necessarily anticipate the problems - you can't necessarily know which new method signatures would be safe and which would cause conflicts. So case 2 above is much more important to me than case 1, since it catches non-obvious errors rather than errors I should have anticipated. My main reason for case 1 is that programmers shouldn't add override flags to every single method just to save thinking about it, since it defeats the whole point of the keyword. But it's just so obviously grossly bad style anyway, like not bothering with indentation or calling your variable var1, var2, var3 and so on. Going off on a tangent, a way of saying 'OK, this is the same signature as a base class method, but this is independent, and gets a separate slot in the virtual table' might be a good idea. Perhaps an alternate application of the 'new' keyword. Or maybe there already is a flag for this, and I forgot. The idea is that, when a derived class developer gets a conflict with a new base class interface, there's a quick (if slightly dirty) fix. The derived class can keep its (possibly also published) interface intact. Anyone needing the base class version can use a call notation indicating that, or else cast to the base class - less than ideal, but maybe better than forcing the derived class developers into renaming and refactoring, and the resulting interface change. -- Remove 'wants' and 'nospam' from e-mail.
Jan 23 2007
parent reply kris <foo bar.com> writes:
Steve Horne wrote:
 On Tue, 23 Jan 2007 00:26:10 -0800, kris <foo bar.com> wrote:
 
 
Steve Horne wrote:
[snip]


And adding a new private member to a base class should not
break any derived classes that might happen to already use the same
signature for a (private or public) method, especially since you may
have no control or even knowledge of those derived classes.

Given that, I'd imagine you'd be a proponent of making 'override' a required keyword? After all, not requiring it will likely lead to the same type of problem when a base-class method is removed. Yet, judging by past record, override is unlikely to ever be required.

Override should only be specified when the intent is to override, IMO. If adding a new public/protected method to a base class results in its accidentally being overridden by a pre-existing conflicting derived-class method, clearly that's bad. So... 1. Specifying override on a method that doesn't override any base class method should be an error. 2. Failing to specify override on a method that does override a base class public/protected method should be an error. I thought that was what D does anyway, and for that matter that it was the whole point of the override keyword

(1) does generate an error, but (2) has never generated one, and requests to make it do so have met with silence from W for years :) Everything else you say I agree with, and thanks for taking the time to respond in such detail. - Kris
Jan 23 2007
parent Steve Horne <stephenwantshornenospam100 aol.com> writes:
On Tue, 23 Jan 2007 09:20:18 -0800, kris <foo bar.com> wrote:

Steve Horne wrote:

 1. Specifying override on a method that doesn't override any base
    class method should be an error.
 2. Failing to specify override on a method that does override a base
    class public/protected method should be an error.


(1) does generate an error, but (2) has never generated one, and 
requests to make it do so have met with silence from W for years :)

Ah - ok. I'm not so sure the override modifier has much value, done this way. I guess I agree - it looks like a missed opportunity. I guess the trouble is that tightening up the rules would be big breaking change. Easy to fix, but a lot of fixing needed. Oh well. -- Remove 'wants' and 'nospam' from e-mail.
Jan 24 2007