[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