lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

On Thu, 2005-02-03 at 05:17, Mark Hamburg wrote:

> The Problem
> -----------
> In single-inheritance languages with super, what super means is:
> 1. Given that we are inside a method M for an object O; and
> 2. Given that M is defined in some class C; and
> 3. Given that we want to send some message X to "super"

This isn't enough specification. Suppose we actually find
the super method, and it calls M2. Do we wish to execute
the M2 found from the complete class, class (O), or the one
found from the class in which M is defined?

In C++ terminology, do we use the virtual table of
the super method resolving the calls made with it,
or continue to use the complete class's virtual table?

In C++ what happens (at compile time) is that

(a) the virtual table *always* refers to the complete
class's overriding methods. Even if the overriding
method is defined in a base class, it is literally
*inherited* into the virtual table (along with an
adjustment to the this pointer so the offsets 
to the private data are correct).

The virtual table is universal.

(b) there is a completely independent static method
call lookup

So the answer is, calls made without scope override
operator to virtual methods always call the current
virtual method. Calls made to non-virtual methods,
or made to any method using scope override operator
do static lookup. So it depends on the call initially,
but if the call is unqualified, and the method is virtual,
the runtime call is not to the method found by static
lookup but the one in the virtual table at the time
of the call.

Note during construction and destruction the virtual
table is adjusted dynamically to reflect the
current object under construction 
or destruction -- which is VERY messy and slow
if not properly optimised.

C++ ALSO allows abstraction: the client does static
lookup in a base class (but the method is virtual
the most derived class method is still called unless
scope override is used).

Python also has dual lookup: you can find a function
in the current object OR in some class (including
a totally unrelated class :) However all methods
are virtual and abstraction is impossible.

Note also Python got multiple inheritance wrong
for a very long time.

'super' is fragile in the sense that if you add a method
into the override list, the method called will be changed:
there is no reliable way to call a particular method.
The same is true for C++ scope overrides, and worse,
the C++ mechanism *incorrectly* names the class
and not the subobject where the search is to start.
This is a design fault in C++ (and also Python).

This fault is not evident with single inheritance.

As I mentioned before the whole thing is a master hack.
and completely unnecessary since it is coping with
an design bug. There is no need for any of it.
The correct technique as mentioned is to split the
method into two:

	class B {
		void B_Method();
		virtul void Method() { B_Method(); }
		void f() { 
			Method(); // virtual call
			B_method(); // non-virtual call
	class D : B {
		void D_Method();
		virtual void Method () { D_Method(); }
		void g() {
			Method(); // virtual call
			B_method(); // Super call
			D_Method(); // Self call

Only someone that had never done any functional programming
wouldn't see why this is necessary: to support self-recursion.
(A self-recursive virtual function must call itself, by specification,
and not some overriding method). Note that if you use a 'super'
hack you need a 'self' hack too, also to undo the virtual dispatch.

The technique described here is clearly better than "super".
When you call B_method() it always calls B_method(),
provided you follow the protocol of course. There's no
question of which method is called, with 'super' it
might change. This also works with multiple inhertance,
where "super" is meaningless drivel, and it fails correctly
on ambiguity.

The point is there is not only no need to use 'sugar'
to specify 'super' it is also a bad idea. It doesn't
generalise to multiple inheritance, nor handle 'self' calls.

The system I outline just works without system support.
No sugar. No special lookups. The way a virtual
function calls the method it overrides is to call
its implementation -- all that you need is a name for it.

But if you REALLY want to have some sugar for 'super'
then -- the above technique defines the implementation.

The compiler has to split all methods into 2 methods.
When you call 'super' you're calling a different method
to a call without 'super'.

One way to do this is something like:

	self:f() -- virtual call
	X.f(self) -- static call to X's f()'s implementation

I can't think straight about this but I'm willing to bet
the existing Lua OO syntax will already handle the
requirement -- the requirement is NOT to do a 'super class
call' but to do a static call, so all we need is
lookup in the class, instead of lookup in the object.

This means that object based (virtual) lookup is implemented
with a meta-hook thing which simply does static lookup from
the current class. Lookup in the class simply
bypasses lookup in the current object.and looks straight
into the desired class table which is found by naming
the class explicitly.

Note this requires classes are also a kind of object.
Since these would be ordinary objects .. some interesting
meta programming tricks could be done for fun :)

SO the basic implementation would be: when constructing
an X object, the __class attribute is set to the class
method table. The destructor does this too!

On a call, you're using the most derived class method
(virtual call). If you want to use a 'super' method,
just use the function in its class table.

Oh -- this is what Python does .. except the current
class method table isn't stored, instead it stores
a pointer into the inheritance graph and does run
time lookup.

Urgg .. hope this makes sense. The bottom line is that
'super' lookup is crap. The distinction you want
is between object based lookup and class based lookup.

John Skaller,
voice: 061-2-9660-0850, 
snail: PO BOX 401 Glebe NSW 2037 Australia
Checkout the Felix programming language