[M3devel] calling an overridden method

Rodney M. Bates rodney_bates at lcwb.coop
Wed Jul 20 18:18:37 CEST 2011



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
>



More information about the M3devel mailing list