digitalmars.D - polymorphism: overloading vs. overriding
- Markus Kranz (34/34) May 07 2006 Following the rule of last surprise: wouldn't it be desirable if the out...
- Mike Parker (8/29) May 07 2006 This line is the key:
- markus_kranz gmx.net (14/34) May 07 2006 I can confirm this at least for Java.
- Bruno Medeiros (6/39) May 07 2006 No, that is wrong, foo *is* an instance of Bar. (yet, it's type contract...
- Mike Parker (5/16) May 07 2006 Yes, yes. Underneath the foo is an instance of Bar. But because, as you
- Bruno Medeiros (15/64) May 07 2006 No, it's not unnatural. For a function to override another, the return
- markus_kranz gmx.net (19/54) May 07 2006 I see Bar.doIt(Bar) does not override Foo.doIt(Foo) given your definitio...
- Bruno Medeiros (13/76) May 07 2006 In http://www.digitalmars.com/d/function.html , "Function Inheritance
- Markus Kranz (5/18) May 07 2006 That's soothing.
- Bruno Medeiros (8/13) May 07 2006 Whoa, I forgot to mention: D doesn't support contravariant parameters,
- Markus Kranz (13/22) May 07 2006 So the actual state is
- Markus Kranz (15/15) May 07 2006 Uh, I didn't know the issue of "covariance versus contravariance" is a t...
- Bruno Medeiros (5/28) Jun 15 2006 I finally took the time to read it, it is indeed a good article.
Following the rule of last surprise: wouldn't it be desirable if the output of the following program would be: Bar.doIt() Bar.doIt(Bar) The actual output Bar.doIt() Foo.doIt(Foo) at least for me is a little bit surprising since the best matching function seems to be Bar.doIt(Bar). As overriding with covariant return types is supported it seems a bit unnatural to me that overriding with covariant arguments does not work. Instead of doing overload resolution before virtual function resolution (what seems to be done now) an implementation of 'covariant arguments' theoretically could be as simple as reversing the order of resolution. What do you think? Regards, Markus -- import std.stdio; class Foo { void doIt() { writefln("Foo.doIt()"); } void doIt(Foo that) { writefln("Foo.doIt(Foo)"); } } class Bar : Foo { //overrides Foo.doIt() void doIt() { writefln("Bar.doIt()"); } //overloads (inherited) Foo.doIt(Foo) void doIt(Bar that) { writefln("Bar.doIt(Bar)"); } } int main() { Foo foo = new Bar(); foo.doIt(); foo.doIt(foo); }
May 07 2006
Markus Kranz wrote:Following the rule of last surprise: wouldn't it be desirable if the output of the following program would be: Bar.doIt() Bar.doIt(Bar) The actual output Bar.doIt() Foo.doIt(Foo) at least for me is a little bit surprising since the best matching function seems to be Bar.doIt(Bar). As overriding with covariant return types is supported it seems a bit unnatural to me that overriding with covariant arguments does not work. Instead of doing overload resolution before virtual function resolution (what seems to be done now) an implementation of 'covariant arguments' theoretically could be as simple as reversing the order of resolution. What do you think?This line is the key: Foo foo = new Bar(); foo is an instance of Foo, not an instance of Bar, so the appropriate method (Foo.doIt(Foo)) is being called. Java and C++ both have the same behavior. What you are suggesting is unintuitive. Not all Foos are Bars, but all Bars are Foos. If you want Bar.doIt to be called when you have a an instance of Foo, you have to downcast.
May 07 2006
In article <e3ki2e$2o7a$1 digitaldaemon.com>, Mike Parker says...Markus Kranz wrote:Sorry, but what I read is that foo is not just a Foo but even a Bar?![The desired output] Bar.doIt() Bar.doIt(Bar) The actual output Bar.doIt() Foo.doIt(Foo) [snip]This line is the key: Foo foo = new Bar(); foo is an instance of Foo, not an instance of Bar, so the appropriate method (Foo.doIt(Foo)) is being called.Java and C++ both have the same behavior.I can confirm this at least for Java. But someone told me D was not Java... ;-)What you are suggesting is unintuitive. Not all Foos are Bars, but all Bars are Foos. If you want Bar.doIt to be called when you have a an instance of Foo, you have to downcast.To my knowledge in D as in Java all member functions are virtual. That is why foo.doIt() actually calls the version in Bar - without any explicit cast. Why do you think the actual behaviour is intuitive while my proposal isn't? I would have understood if Foo.doIt() Foo.doIt(Foo) got your vote. Regards Markus
May 07 2006
Mike Parker wrote:Markus Kranz wrote:No, that is wrong, foo *is* an instance of Bar. (yet, it's type contract is only that it is a Foo) -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#DFollowing the rule of last surprise: wouldn't it be desirable if the output of the following program would be: Bar.doIt() Bar.doIt(Bar) The actual output Bar.doIt() Foo.doIt(Foo) at least for me is a little bit surprising since the best matching function seems to be Bar.doIt(Bar). As overriding with covariant return types is supported it seems a bit unnatural to me that overriding with covariant arguments does not work. Instead of doing overload resolution before virtual function resolution (what seems to be done now) an implementation of 'covariant arguments' theoretically could be as simple as reversing the order of resolution. What do you think?This line is the key: Foo foo = new Bar(); foo is an instance of Foo, not an instance of Bar,
May 07 2006
Bruno Medeiros wrote:Mike Parker wrote:Yes, yes. Underneath the foo is an instance of Bar. But because, as you say, the type contract is that of Foo then you are effectively dealing with a Foo and not a Bar. It should only be treated as a Bar when you downcast.This line is the key: Foo foo = new Bar(); foo is an instance of Foo, not an instance of Bar,No, that is wrong, foo *is* an instance of Bar. (yet, it's type contract is only that it is a Foo)
May 07 2006
Markus Kranz wrote:Following the rule of last surprise: wouldn't it be desirable if the output of the following program would be: Bar.doIt() Bar.doIt(Bar) The actual output Bar.doIt() Foo.doIt(Foo) at least for me is a little bit surprising since the best matching function seems to be Bar.doIt(Bar). As overriding with covariant return types is supported it seems a bit unnatural to me that overriding with covariant arguments does not work. Instead of doing overload resolution before virtual function resolution (what seems to be done now) an implementation of 'covariant arguments' theoretically could be as simple as reversing the order of resolution. What do you think? Regards, Markus -- import std.stdio; class Foo { void doIt() { writefln("Foo.doIt()"); } void doIt(Foo that) { writefln("Foo.doIt(Foo)"); } } class Bar : Foo { //overrides Foo.doIt() void doIt() { writefln("Bar.doIt()"); } //overloads (inherited) Foo.doIt(Foo) void doIt(Bar that) { writefln("Bar.doIt(Bar)"); } } int main() { Foo foo = new Bar(); foo.doIt(); foo.doIt(foo); }No, it's not unnatural. For a function to override another, the return type must be covariant, but the parameters must be contravariant. It's quite natural to be this way, in fact, it's the only correct way (other than invariant parameters). The reason why it is so, is because for a function to override another, it must handle all (or more) inputs than the previous function. Consider, in your example, the following addition class Baz : Foo { } Foo.doIt(Foo that) can handle an argument of type Baz, but Bar.doIt(Bar that) cannot handle such an argument. Thus Bar.doIt doesn't override Foo.doIt -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
May 07 2006
In article <e3kjf0$2q09$3 digitaldaemon.com>, Bruno Medeiros says...Markus Kranz wrote:I see Bar.doIt(Bar) does not override Foo.doIt(Foo) given your definition of overriding. But given your szenario: Bar bar = new Bar(); Baz baz = new Baz(); bar.doIt(baz); AFAIS should work perfectly (cause baz is a Baz and so a Foo and Bar inherits from Foo the function doIt(Foo))... ..and does so in Java (but that's no argument <g>) ..but fails in D <hmpf> test.d(23): function test.Bar.doIt () does not match argument types (Baz) test.d(23): cannot implicitly convert expression (baz) of type test.Baz to test.Bar Of course what dmd says is true, but why can't it implicitly convert expression (baz) of type test.Baz to test.Foo? Does 'overriding' in D does not mean the same as in Java? Regards, Markus[snip] -- import std.stdio; class Foo { void doIt() { writefln("Foo.doIt()"); } void doIt(Foo that) { writefln("Foo.doIt(Foo)"); } } class Bar : Foo { //overrides Foo.doIt() void doIt() { writefln("Bar.doIt()"); } //overloads (inherited) Foo.doIt(Foo) void doIt(Bar that) { writefln("Bar.doIt(Bar)"); } } int main() { Foo foo = new Bar(); foo.doIt(); foo.doIt(foo); }No, it's not unnatural. For a function to override another, the return type must be covariant, but the parameters must be contravariant. It's quite natural to be this way, in fact, it's the only correct way (other than invariant parameters). The reason why it is so, is because for a function to override another, it must handle all (or more) inputs than the previous function. Consider, in your example, the following addition class Baz : Foo { } Foo.doIt(Foo that) can handle an argument of type Baz, but Bar.doIt(Bar that) cannot handle such an argument. Thus Bar.doIt doesn't override Foo.doIt
May 07 2006
markus_kranz gmx.net wrote:In article <e3kjf0$2q09$3 digitaldaemon.com>, Bruno Medeiros says...In http://www.digitalmars.com/d/function.html , "Function Inheritance and Overriding" it is said: "However, when doing overload resolution, the functions in the base class are not considered:" If that is the ideal behavior, well, that I'm not sure...Markus Kranz wrote:I see Bar.doIt(Bar) does not override Foo.doIt(Foo) given your definition of overriding. But given your szenario: Bar bar = new Bar(); Baz baz = new Baz(); bar.doIt(baz); AFAIS should work perfectly (cause baz is a Baz and so a Foo and Bar inherits from Foo the function doIt(Foo))... ...and does so in Java (but that's no argument <g>) ...but fails in D <hmpf> test.d(23): function test.Bar.doIt () does not match argument types (Baz) test.d(23): cannot implicitly convert expression (baz) of type test.Baz to test.Bar[snip] -- import std.stdio; class Foo { void doIt() { writefln("Foo.doIt()"); } void doIt(Foo that) { writefln("Foo.doIt(Foo)"); } } class Bar : Foo { //overrides Foo.doIt() void doIt() { writefln("Bar.doIt()"); } //overloads (inherited) Foo.doIt(Foo) void doIt(Bar that) { writefln("Bar.doIt(Bar)"); } } int main() { Foo foo = new Bar(); foo.doIt(); foo.doIt(foo); }No, it's not unnatural. For a function to override another, the return type must be covariant, but the parameters must be contravariant. It's quite natural to be this way, in fact, it's the only correct way (other than invariant parameters). The reason why it is so, is because for a function to override another, it must handle all (or more) inputs than the previous function. Consider, in your example, the following addition class Baz : Foo { } Foo.doIt(Foo that) can handle an argument of type Baz, but Bar.doIt(Bar that) cannot handle such an argument. Thus Bar.doIt doesn't override Foo.doItOf course what dmd says is true, but why can't it implicitly convert expression (baz) of type test.Baz to test.Foo?Even if you put a cast(Foo) on the argument, it won't compile, because it only considers the overloads (Bar.doIt) of the child class (Bar) Once again, if that is the ideal behavior, I'm not sure...Does 'overriding' in D does not mean the same as in Java?It does. The difference in behavior from Java, is what I said above. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
May 07 2006
In article <e3knre$2vp1$1 digitaldaemon.com>, Bruno Medeiros says...In http://www.digitalmars.com/d/function.html , "Function Inheritance and Overriding" it is said: "However, when doing overload resolution, the functions in the base class are not considered:" If that is the ideal behavior, well, that I'm not sure...Okay. At least the considered alias construction works.Of course what dmd says is true, but why can't it implicitly convert expression (baz) of type test.Baz to test.Foo?Even if you put a cast(Foo) on the argument, it won't compile, because it only considers the overloads (Bar.doIt) of the child class (Bar) Once again, if that is the ideal behavior, I'm not sure...That's soothing. Regards MarkusDoes 'overriding' in D does not mean the same as in Java?It does. The difference in behavior from Java, is what I said above.
May 07 2006
Bruno Medeiros wrote:No, it's not unnatural. For a function to override another, the return type must be covariant, but the parameters must be contravariant. It's quite natural to be this way, in fact, it's the only correct way (other than invariant parameters).Whoa, I forgot to mention: D doesn't support contravariant parameters, only covariant return types (in other words, a covariant parameter will create a new overload, and not an override). I don't know why I got the idea that D supported that. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
May 07 2006
In article <e3kp8d$31ag$1 digitaldaemon.com>, Bruno Medeiros says...Bruno Medeiros wrote:So the actual state is (1) the scope of overload resolution is the current class (and optionally superclasses using alias) (2) the scope of virtual function resolution is the current class and 'childwards' Overloading and overriding on their own seem to be simple concepts. It remains the question: as in D is it enough to implement both concepts separately although they are strongly related? (In both concepts we deal with multiple functions having a common name and search for the 'best' matching instance.) Regards, MarkusNo, it's not unnatural. For a function to override another, the return type must be covariant, but the parameters must be contravariant. It's quite natural to be this way, in fact, it's the only correct way (other than invariant parameters).Whoa, I forgot to mention: D doesn't support contravariant parameters, only covariant return types (in other words, a covariant parameter will create a new overload, and not an override). I don't know why I got the idea that D supported that.
May 07 2006
Uh, I didn't know the issue of "covariance versus contravariance" is a topic of continuing debate: http://c2.com/cgi/wiki?ContraVsCoVariance and especially http://www.cs.trinity.edu/~mlewis/CSCI3294-F01/Papers/p431-castagna.pdf where I found on page 10: "At this point, we are able to make precise the roles played by covariance and contravariance in subtyping: contravariance is the correct rule when you want to substitute a function of a given type for another one of a different type; covariance is the correct condition when you want to specialize (in object-oriented jargon "override") a branch of an overloaded function by one with a smaller input type." Regards, Markus
May 07 2006
Markus Kranz wrote:Uh, I didn't know the issue of "covariance versus contravariance" is a topic of continuing debate: http://c2.com/cgi/wiki?ContraVsCoVariance and especially http://www.cs.trinity.edu/~mlewis/CSCI3294-F01/Papers/p431-castagna.pdf where I found on page 10: "At this point, we are able to make precise the roles played by covariance and contravariance in subtyping: contravariance is the correct rule when you want to substitute a function of a given type for another one of a different type; covariance is the correct condition when you want to specialize (in object-oriented jargon "override") a branch of an overloaded function by one with a smaller input type." Regards, MarkusI finally took the time to read it, it is indeed a good article. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 15 2006