[M3devel] calling an overridden method

Mika Nystrom mika at async.caltech.edu
Thu Jul 21 09:27:43 CEST 2011


Section 6, under "Designators".

    Mika

Peter McKinna writes:
>--0015174c40fe49b5ad04a88e39c4
>Content-Type: text/plain; charset=ISO-8859-1
>
>Forgive my ignorance, but where in the report does it allow A.m(b,xxx)
>where that A is a type
>
>I just compiled it and it works fine but its a syntactic subtlety that I
>have sadly been unaware.
>
>Regards Peter
>
>On Thu, Jul 21, 2011 at 2:18 AM, Rodney M. Bates <rodney_bates at lcwb.coop>wrote
>:
>
>>
>>
>> On 07/20/2011 02:44 AM, Mika Nystrom wrote:
>>
>>> Right, a shadowed method is a new method of the same name, just like
>>> Algol block structure.  An overridden method is a different binding for
>>> the same method.  If you override a method, you can't call the old one
>>> without using the special A.T.m(a,...) syntax, since there's no binding
>>> for the old one within the type record for your object.  The A.T.m(...)
>>> syntax works whether the method is shadowed or overridden.  The widening
>>> only works for shadowed methods.
>>>
>>> The clever thing is that the method dispatch is based on the known type
>>> of the object within the context that the method is called, not where the
>>> object type is declared.
>>>
>>> This distinction between overriding and shadowing I think is one of the
>>> most brilliant aspects of Modula-3: it avoids the problem that you
>>> create an unwanted method override by adding a new method of the same name
>>> (whether in a supertype or a subtype).
>>>
>>
>> This is probably my single favorite example of how C++ is badly
>> designed for practical programming and, more generally, why syntactic
>> explicitness is important.
>>
>> C++ has both overrides and new methods, but it is *very* hard for a
>> programmer to see the difference.  Syntactically, they look identical.
>>
>> For a member function declaration to be an override, you need:
>> 1) It has the same simple name as the original.
>> 2) It has an equivalent signature to the original.
>> 3) The original is 'virtual', which in this context means overridable.
>>
>> Equivalence of signatures is complicated, as there are several rules
>> about selected ways they can differ and still be equivalent.  For
>> example, int *p and int p[] are equivalent.  There can be overloaded
>> (same name, different signatures) original methods, from the same or
>> different superclasses, to be considered.  And then this allows what
>> would, I suppose, be called "overloaded overrides", as well as a mix
>> of overloaded new methods and overrides, all with the same name.
>>
>> 'virtual' has at least two very different meanings, one relevant here
>> and one regarding repeated inheritance over multiple paths.  The
>> virtual property can come from anywhere in the inheritance hierarchy,
>> and can change between the original method declaration and the
>> subclass we are trying to read.
>>
>> Shadowing can also involve signature equivalence as well as name
>> equality.  AFAIK, you can't shadow with the same name and signature,
>> if the relevant inherited method is virtual.  But if not, a same-name,
>> signature-equivalent member function is a shadow.
>>
>> Meanwhile, linking up to a precursor to be overridden involves looking
>> at signatures of same-named methods from all superclasses, whereas
>> overload resolution in calls considers only same-named candidates from
>> the same class, before signatures are considered.
>>
>> The interactions with static member functions make things even more
>> complicated.
>>
>> None of this is syntactically explicit.  In contrast, Modula-3 says
>> either "METHODS" or "OVERRIDES" explicitly.  Moreover, even without
>> these tags, new methods always have signatures, while overrides do
>> not.
>>
>> In my experience, very few C++ programmers, outside of compiler
>> writers and language committee members, understand these semantic
>> rules.  And they do predictably get surprised when something turns out
>> to dispatch when they expected it not to or vice versa.  When this
>> happens, rather than figure the language out, they just make a note to
>> self to use some coding convention that avoids getting anywhere close,
>> e.g., never using the same name for different methods, only for
>> overrides. (Well, OK, that's probably a good practice anyway, but you
>> see my point, I think.)
>>
>>
>>
>>>      Mika
>>>
>>>
>>> Hendrik Boom writes:
>>>
>>>> On Tue, Jul 19, 2011 at 01:26:14PM -0400, Hendrik Boom wrote:
>>>>
>>>>> On Tue, Jul 19, 2011 at 07:11:05AM -0700, Mika Nystrom wrote:
>>>>>
>>>>>> Sorry I'm being confusing here.
>>>>>>
>>>>>> The code is right but I think your question is a bit confusing.
>>>>>>
>>>>>> With your init example I don't think you're talking about an overridden
>>>>>> init method but a shadowed method.
>>>>>>
>>>>>
>>>>> A 'shadowed' method.  Is that what you call what's essentially a new
>>>>> method that happens to have the same name?  Just like the way a variable
>>>>> in a nested block that has the same name as one in an outer block would
>>>>> be a new variable (I'm not sure Modula 3 allows this, the way C and the
>>>>> Algols do; I find it leads to confusing code).
>>>>>
>>>>> Init is the one I've seen explained in documentation.  It's  not what I
>>>>> want, because that trick provides no inheritance.
>>>>>
>>>>>  You can't call an overridden method
>>>>>> through the widening trick, but you *can* call a shadowed method like
>>>>>> that.
>>>>>>
>>>>>>      Mika
>>>>>>
>>>>>> Mika Nystrom writes:
>>>>>>
>>>>>>>
>>>>>>> TYPE A = OBJECT METHODS m(xxx) END;
>>>>>>>
>>>>>>> TYPE B = A OBJECT METHODS m(xxx) END;
>>>>>>>
>>>>>>
>>>>> This is, if I understand the terminology, a declaratin of m as a shadowd
>>>>> method.  It's not what I want, because I  intend that under normal
>>>>> circumstances, if an B object is used in a context where its static type
>>>>> is known as A, I still want the normal overriding mechanism to ensure
>>>>> that B's m is the one called.
>>>>>
>>>>> I mean something like
>>>>>
>>>>> TYPE B = A OBJECT OVERRIDES m := ... END;
>>>>>
>>>>>
>>>>>>> b := NEW(B);
>>>>>>>
>>>>>>> A.m(b,xxx)
>>>>>>>
>>>>>>
>>>>> So this is a way to specifically get the m that belongs with A?  Will
>>>>> this work with the way I want to define B and m?
>>>>>
>>>>> as in
>>>>>
>>>>> TYPE A = OBJECT METHODS m(xxx) := bar END;
>>>>>
>>>>> TYPE B = A OBJECT OVERRIDES m := foo END;
>>>>>
>>>>> PROCEDURE foo(self : B, ....) =
>>>>> BEGIN
>>>>>   ...
>>>>>   ...
>>>>>    A.m(self, ...)   (* and this ends up calling bar? *)
>>>>>   ...
>>>>> END foo;
>>>>>
>>>>
>>>> Well. I tried my version, and it works.  Thanks for the advice.
>>>>
>>>> -- hendrik
>>>>
>>>>
>>>>>
>>>>>>>      Mika
>>>>>>>
>>>>>>> Hendrik Boom writes:
>>>>>>>
>>>>>>>> I have a module containing a parent class, and another containing a
>>>>>>>> child class.
>>>>>>>>
>>>>>>>> The parent class contains a method 'foo', which is to be overridden
>>>>>>>> in
>>>>>>>> the child class.
>>>>>>>>
>>>>>>>> But in implementing 'foo' in the child class I want to call the
>>>>>>>> parent's
>>>>>>>>
>>>>>>>
>>>>  method.
>>>>>>>>
>>>>>>>> Now with the method 'init' there's a trick where in the child's init,
>>>>>>>> you WIDEN self to the parent's type and then call its init.
>>>>>>>> This works because 'init' isn't overridden in an OVERRIDES clause,
>>>>>>>> but is defined as a new method that happens to have the same name.
>>>>>>>>
>>>>>>>> Is there any way to do this with 'foo', where the whole point is that
>>>>>>>> it be an overridden method and not a new one?
>>>>>>>>
>>>>>>>> Do I have to do something like covertly exporting the PROCEDURE that
>>>>>>>> implements 'foo' in the parent's module so that it can be called
>>>>>>>> directly? That would seem to be a violation of modular design.  Or is
>>>>>>>> violating modular design exactly what I'm really trying to do here?
>>>>>>>>
>>>>>>>> -- hendrik
>>>>>>>>
>>>>>>>
>>>
>
>--0015174c40fe49b5ad04a88e39c4
>Content-Type: text/html; charset=ISO-8859-1
>Content-Transfer-Encoding: quoted-printable
>
>Forgive my ignorance, but where in the report does it allow A.m(b,xxx)=A0 w=
>here that A is a type<br><br>I just compiled it and it works fine but its a=
> syntactic subtlety that I have sadly been unaware.<br><br>Regards Peter<br=
>>
><br><div class=3D"gmail_quote">On Thu, Jul 21, 2011 at 2:18 AM, Rodney M. B=
>ates <span dir=3D"ltr"><<a href=3D"mailto:rodney_bates at lcwb.coop">rodney=
>_bates at lcwb.coop</a>></span> wrote:<br><blockquote class=3D"gmail_quote"=
> style=3D"border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.=
>8ex; padding-left: 1ex;">
><br>
><br>
>On 07/20/2011 02:44 AM, Mika Nystrom wrote:<br>
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
>Right, a shadowed method is a new method of the same name, just like<br>
>Algol block structure. =A0An overridden method is a different binding for<b=
>r>
>the same method. =A0If you override a method, you can't call the old on=
>e<br>
>without using the special A.T.m(a,...) syntax, since there's no binding=
><br>
>for the old one within the type record for your object. =A0The A.T.m(...)<b=
>r>
>syntax works whether the method is shadowed or overridden. =A0The widening<=
>br>
>only works for shadowed methods.<br>
><br>
>The clever thing is that the method dispatch is based on the known type<br>
>of the object within the context that the method is called, not where the<b=
>r>
>object type is declared.<br>
><br>
>This distinction between overriding and shadowing I think is one of the<br>
>most brilliant aspects of Modula-3: it avoids the problem that you<br>
>create an unwanted method override by adding a new method of the same name<=
>br>
>(whether in a supertype or a subtype).<br>
></blockquote>
><br>
>This is probably my single favorite example of how C++ is badly<br>
>designed for practical programming and, more generally, why syntactic<br>
>explicitness is important.<br>
><br>
>C++ has both overrides and new methods, but it is *very* hard for a<br>
>programmer to see the difference. =A0Syntactically, they look identical.<br=
>>
><br>
>For a member function declaration to be an override, you need:<br>
>1) It has the same simple name as the original.<br>
>2) It has an equivalent signature to the original.<br>
>3) The original is 'virtual', which in this context means overridab=
>le.<br>
><br>
>Equivalence of signatures is complicated, as there are several rules<br>
>about selected ways they can differ and still be equivalent. =A0For<br>
>example, int *p and int p[] are equivalent. =A0There can be overloaded<br>
>(same name, different signatures) original methods, from the same or<br>
>different superclasses, to be considered. =A0And then this allows what<br>
>would, I suppose, be called "overloaded overrides", as well as a =
>mix<br>
>of overloaded new methods and overrides, all with the same name.<br>
><br>
>'virtual' has at least two very different meanings, one relevant he=
>re<br>
>and one regarding repeated inheritance over multiple paths. =A0The<br>
>virtual property can come from anywhere in the inheritance hierarchy,<br>
>and can change between the original method declaration and the<br>
>subclass we are trying to read.<br>
><br>
>Shadowing can also involve signature equivalence as well as name<br>
>equality. =A0AFAIK, you can't shadow with the same name and signature,<=
>br>
>if the relevant inherited method is virtual. =A0But if not, a same-name,<br=
>>
>signature-equivalent member function is a shadow.<br>
><br>
>Meanwhile, linking up to a precursor to be overridden involves looking<br>
>at signatures of same-named methods from all superclasses, whereas<br>
>overload resolution in calls considers only same-named candidates from<br>
>the same class, before signatures are considered.<br>
><br>
>The interactions with static member functions make things even more<br>
>complicated.<br>
><br>
>None of this is syntactically explicit. =A0In contrast, Modula-3 says<br>
>either "METHODS" or "OVERRIDES" explicitly. =A0Moreover=
>, even without<br>
>these tags, new methods always have signatures, while overrides do<br>
>not.<br>
><br>
>In my experience, very few C++ programmers, outside of compiler<br>
>writers and language committee members, understand these semantic<br>
>rules. =A0And they do predictably get surprised when something turns out<br=
>>
>to dispatch when they expected it not to or vice versa. =A0When this<br>
>happens, rather than figure the language out, they just make a note to<br>
>self to use some coding convention that avoids getting anywhere close,<br>
>e.g., never using the same name for different methods, only for<br>
>overrides. (Well, OK, that's probably a good practice anyway, but you<b=
>r>
>see my point, I think.)<br>
><br>
><br>
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
><br>
> =A0 =A0 =A0Mika<br>
><br>
><br>
>Hendrik Boom writes:<br>
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
>On Tue, Jul 19, 2011 at 01:26:14PM -0400, Hendrik Boom wrote:<br>
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
>On Tue, Jul 19, 2011 at 07:11:05AM -0700, Mika Nystrom wrote:<br>
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
>Sorry I'm being confusing here.<br>
><br>
>The code is right but I think your question is a bit confusing.<br>
><br>
>With your init example I don't think you're talking about an overri=
>dden<br>
>init method but a shadowed method.<br>
></blockquote>
><br>
>A 'shadowed' method. =A0Is that what you call what's essentiall=
>y a new<br>
>method that happens to have the same name? =A0Just like the way a variable<=
>br>
>in a nested block that has the same name as one in an outer block would<br>
>be a new variable (I'm not sure Modula 3 allows this, the way C and the=
><br>
>Algols do; I find it leads to confusing code).<br>
><br>
>Init is the one I've seen explained in documentation. =A0It's =A0no=
>t what I<br>
>want, because that trick provides no inheritance.<br>
><br>
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
>You can't call an overridden method<br>
>through the widening trick, but you *can* call a shadowed method like<br>
>that.<br>
><br>
> =A0 =A0 =A0Mika<br>
><br>
>Mika Nystrom writes:<br>
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
><br>
>TYPE A =3D OBJECT METHODS m(xxx) END;<br>
><br>
>TYPE B =3D A OBJECT METHODS m(xxx) END;<br>
></blockquote></blockquote>
><br>
>This is, if I understand the terminology, a declaratin of m as a shadowd<br=
>>
>method. =A0It's not what I want, because I =A0intend that under normal<=
>br>
>circumstances, if an B object is used in a context where its static type<br=
>>
>is known as A, I still want the normal overriding mechanism to ensure<br>
>that B's m is the one called.<br>
><br>
>I mean something like<br>
><br>
>TYPE B =3D A OBJECT OVERRIDES m :=3D ... END;<br>
><br>
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><blockquote class=
>=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, 204, 204); margin=
>: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
>
><br>
>b :=3D NEW(B);<br>
><br>
>A.m(b,xxx)<br>
></blockquote></blockquote>
><br>
>So this is a way to specifically get the m that belongs with A? =A0Will<br>
>this work with the way I want to define B and m?<br>
><br>
>as in<br>
><br>
>TYPE A =3D OBJECT METHODS m(xxx) :=3D bar END;<br>
><br>
>TYPE B =3D A OBJECT OVERRIDES m :=3D foo END;<br>
><br>
>PROCEDURE foo(self : B, ....) =3D<br>
>BEGIN<br>
> =A0 ...<br>
> =A0 ...<br>
> =A0 =A0A.m(self, ...) =A0 (* and this ends up calling bar? *)<br>
> =A0 ...<br>
>END foo;<br>
></blockquote>
><br>
>Well. I tried my version, and it works. =A0Thanks for the advice.<br>
><br>
>-- hendrik<br>
><br>
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
><br>
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><blockquote class=
>=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, 204, 204); margin=
>: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
>
><br>
> =A0 =A0 =A0Mika<br>
><br>
>Hendrik Boom writes:<br>
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
>I have a module containing a parent class, and another containing a<br>
>child class.<br>
><br>
>The parent class contains a method 'foo', which is to be overridden=
> in<br>
>the child class.<br>
><br>
>But in implementing 'foo' in the child class I want to call the par=
>ent's<br>
></blockquote></blockquote></blockquote></blockquote>
><br>
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><blockquote class=
>=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, 204, 204); margin=
>: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
><blockquote class=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, =
>204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><blockquote class=
>=3D"gmail_quote" style=3D"border-left: 1px solid rgb(204, 204, 204); margin=
>: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
>
>method.<br>
><br>
>Now with the method 'init' there's a trick where in the child&#=
>39;s init,<br>
>you WIDEN self to the parent's type and then call its init.<br>
>This works because 'init' isn't overridden in an OVERRIDES clau=
>se,<br>
>but is defined as a new method that happens to have the same name.<br>
><br>
>Is there any way to do this with 'foo', where the whole point is th=
>at<br>
>it be an overridden method and not a new one?<br>
><br>
>Do I have to do something like covertly exporting the PROCEDURE that<br>
>implements 'foo' in the parent's module so that it can be calle=
>d<br>
>directly? That would seem to be a violation of modular design. =A0Or is<br>
>violating modular design exactly what I'm really trying to do here?<br>
><br>
>-- hendrik<br>
></blockquote></blockquote></blockquote></blockquote></blockquote>
><br>
></blockquote>
></blockquote></div><br>
>
>--0015174c40fe49b5ad04a88e39c4--



More information about the M3devel mailing list