[M3devel] calling an overridden method

Peter McKinna peter.mckinna at gmail.com
Thu Jul 21 08:11:46 CEST 2011


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
>>>>>>>
>>>>>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://m3lists.elegosoft.com/pipermail/m3devel/attachments/20110721/3d3f637e/attachment-0002.html>


More information about the M3devel mailing list