lua-users home
lua-l archive

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


on 2/2/05 12:40 AM, PA at petite.abeille@gmail.com wrote:

> Hmmm... either I don't understand what you mean or we are not talking
> about the same thing. Or perhaps both :)

You've taken super out, but I've been trying to find a way to express the
issue clearly and I think I've now homed in on it so I'm going to try one
last time. I promise that this is my last attempt. I'm only posting it
because I think I've figured out how to express the issue with some
precision.

Terminology
-----------
Methods are implementations of messages.

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"

Then:

Start the lookup process for a method implementing X at the superclass of C
but send it O as "self".

In particular, observe that class( self ) need not be C. C will exist
somewhere in the inheritance chain for class( self ), but it may not be
equal to class( self ).

This means that a call to super can't determine what it needs from self
alone. It needs to know the class in which the currently executing method
was defined.

In a prototype-based system, a similar issue applies because we need to know
in what object a method was implemented but that object may not be the same
as the value of self. On a super call, we start the lookup at the object
implementing the current method, but we preserve the value of self.

Solutions
---------
Cooperative subclassing works by having one name the class at which to start
searching rather than using a special "super" keyword:

    function Derived:method()
        Base.method( self )
    end

Base doesn't need to be the actual class providing the implementation. It
just needs to be the parent class of Derived.

The problem with cooperative subclassing is that if you change the parentage
of Derived, you need to remember to update all references to Base. You can
work around this by doing things like:

    local super = Base

    Derived = class{
        super,
        method = function( self )
            super.method( self )
        end
    }

As a bonus, this will execute somewhat more efficiently.

Or one could count on classes knowing their superclass and write:

    function Derived:method()
        Derived.superclass.method( self )
    end

This can extend into a scheme to handle more complex inheritance structures
allowing one to write:

    function Derived:method()
        Derived:superclass( self ).method( self )
    end

One can also capture the class in a function environment with a special name
and bind that environment to the methods. Doing so probably isn't worth it.

There are other games one can play if one is prepared to create closures and
tables the fly, but those can significantly raise the execution cost and
they basically map to the same underlying implementations.

Mark