Thread overview | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
May 07, 2006 polymorphism: overloading vs. overriding | ||||
---|---|---|---|---|
| ||||
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 Re: polymorphism: overloading vs. overriding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Markus Kranz | 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 Re: polymorphism: overloading vs. overriding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Markus Kranz | 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 Re: polymorphism: overloading vs. overriding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Mike Parker | In article <e3ki2e$2o7a$1@digitaldaemon.com>, Mike Parker says... >Markus Kranz wrote: >> [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. Sorry, but what I read is that foo is not just a Foo but even a Bar?! >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 Re: polymorphism: overloading vs. overriding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Mike Parker | Mike Parker wrote: > 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, 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#D |
May 07, 2006 Re: polymorphism: overloading vs. overriding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Bruno Medeiros | In article <e3kjf0$2q09$3@digitaldaemon.com>, Bruno Medeiros says... >Markus Kranz wrote: >> [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 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 |
May 07, 2006 Re: polymorphism: overloading vs. overriding | ||||
---|---|---|---|---|
| ||||
Posted in reply to markus_kranz | markus_kranz@gmx.net wrote: > In article <e3kjf0$2q09$3@digitaldaemon.com>, Bruno Medeiros says... >> Markus Kranz wrote: >>> [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 > > 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 > 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... > 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... > 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 Re: polymorphism: overloading vs. overriding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Bruno Medeiros | 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 Re: polymorphism: overloading vs. overriding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Bruno Medeiros | 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... > >> 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... Okay. At least the considered alias construction works. >> 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. That's soothing. Regards Markus |
May 07, 2006 Re: polymorphism: overloading vs. overriding | ||||
---|---|---|---|---|
| ||||
Posted in reply to Bruno Medeiros | In article <e3kp8d$31ag$1@digitaldaemon.com>, Bruno Medeiros says... > >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. 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, Markus |
Copyright © 1999-2021 by the D Language Foundation