Standard Object Reflection (was Re: OOP in daydream...)

classic Classic list List threaded Threaded
23 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Standard Object Reflection (was Re: OOP in daydream...)

Steve Donovan-2
Although there's more than one way to skin the OOP
cat in Lua, it would be useful to have some conventions.

For example, consider a class browser.  Such code can't
even assume that the methods are stashed in the
object's metatable!  One solution is to expect class
systems to export some standard functions:

1)  _classes_defined
A user-defined iterator for finding all the classes defined
in the system.  (If it isn't defined, we can probably cope
by browsing the global environment recursively)

2) _base
Any table intended to be used as a class will export
a method _base, which may of course return nil.

3) _methods
Any class must define this iterator, which gives the
available methods; from there, the debug interface
could give us where they're physically defined.
(Whether this gives _all_ inherited methods, or just methods
defined by this class, is an interesting question.
I'd say this should be an option)

So no matter how one sets up one's object system,
such a convention would allow general tools 
to be built that don't require detailed knowledge
of the object system.

steve d.


Reply | Threaded
Open this post in threaded view
|

Re: Standard Object Reflection (was Re: OOP in daydream...)

Petite Abeille

On Jan 19, 2005, at 11:04, Steve Donovan wrote:

So no matter how one sets up one's object system,
such a convention would allow general tools
to be built that don't require detailed knowledge
of the object system.

Well... yes and no... as you have mentioned, you need to agree on some form of standardized metadata one way or another...

For what it's worth, here is what the Objective-C runtime does:

objc_getClassList() // getting all the classes in the runtime...

A class is defined as a simple C structure:

struct objc_class
{			
	struct objc_class *isa;	
	struct objc_class *super_class;	
	const char *name;		
	struct objc_ivar_list *ivars;
	struct objc_method_list **methodLists;
 	struct objc_protocol_list *protocols;
};

You then have some functions to access those class informations:

class_nextMethodList()		// get all the methods of an object

class_getInstanceMethod()	//  get information about an instance method
class_getClassMethod		// get information about a class method

Out of those, you can finally build a method signature:

http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ ObjC_classic/Classes/NSMethodSignature.html

That's pretty much it.

See the attachments for the actual C headers. Food for thoughts :)

Cheers

--
PA
http://alt.textdrive.com/



Attachment: objc-runtime.h
Description: Binary data

Attachment: objc-class.h
Description: Binary data






Reply | Threaded
Open this post in threaded view
|

RES: Standard Object Reflection (was Re: OOP in daydream...)

André de Leiradella
In reply to this post by Steve Donovan-2
[hidden email] wrote:
> Although there's more than one way to skin the OOP
> cat in Lua, it would be useful to have some conventions.
> 
> For example, consider a class browser.  Such code can't
> even assume that the methods are stashed in the
> object's metatable!  One solution is to expect class
> systems to export some standard functions:
> 
> 1)  _classes_defined
> A user-defined iterator for finding all the classes defined
> in the system.  (If it isn't defined, we can probably cope
> by browsing the global environment recursively)
> 
> 2) _base
> Any table intended to be used as a class will export
> a method _base, which may of course return nil.
> 
> 3) _methods
> Any class must define this iterator, which gives the
> available methods; from there, the debug interface
> could give us where they're physically defined.
> (Whether this gives _all_ inherited methods, or just methods defined
> by this class, is an interesting question. I'd say this should be an
> option)  
> 
> So no matter how one sets up one's object system,
> such a convention would allow general tools
> to be built that don't require detailed knowledge
> of the object system.
> 
> steve d.

I like your idea. Class systems could be packages that follow some
conventions to allow getting information of a particular system and to
make interoperability possible (e.g. using one class system to subclass
a class of another class system).

A class system package could return a table with some defined functions:

----------------8<----------------
local function _classes_defined()
	-- returns an iterator that will return
	-- each class defined in this system
end

local function _super_of(class)
	-- returns an iterator that will return
	-- each super class of the defined class
	-- (makes multiple inheritance possible)
end

local function _methods_of(class, inherited)
	-- returns an iterator that will return
	-- each method of the class, climbing up
	-- the inheritance tree of the class if
	-- the parameter inherited is true
end

local package = {
	classes_defined = _classes_defined,
	super_of = _super_of,
	methods_of = _methods_of
}

return package
----------------8<----------------

To make interoperability possible, some other functions have to be
defined:

----------------8<----------------
local function _inherited_method(class, method)
	-- returns a function that is the given
	-- method of some super class of class
	-- or nil if no such method exists
end

local function _new(class, constructor, ...)
	-- returns a new instance of class calling
	-- the provided constructor with the
	-- ... arguments or nil if the class
	-- wasn't defined in this system
end

local package = {
	classes_defined  = _classes_defined,
	super_of         = _super_of,
	methods_of       = _methods_of,
	inherited_method = _inherited_method,
	new              = _new
}

return package
----------------8<----------------

Also to make interoperability possible, each class must have a "private"
_PACKAGE field which points to the package table of the class system
where the class was defined, and each instance must have a "private"
_CLASS field which points to the class it's an instance of.

Helper functions can be written to make life easier when using
simultaneous class systems:

----------------8<----------------
function new(class, constructor, ...)
	return class._PACKAGE.new(class, constructor, ...)

function inherited(self, method, ...)
	local func = self._CLASS._PACKAGE.inherited_method(self._CLASS,
method)
	return func(self, ...)
end

-- in my class system I call "class" to
-- define a new class, passing it's super
-- classes as arguments
MirroredSurface = class(Surface)

-- override a constructor
function MirroredSurface:load(fileName)
	-- call the inherited constructor
	inherited(self, "load", fileName)
	-- call a function from a package that
	-- will mirror the image, surface is
	-- an userdata created by the inherited
	-- constructor
	SDL_gfx.mirror(self.surface)
end

Screen = new(Screen, 'init', 640, 480)
background = new(Surface, 'load', 'background.png')
joeLeft = new(Surface, 'load', 'joe.png')
joeRight = new(MirroredSurface, 'load', 'joe.png')

background:blit(screen, 0, 0)
joeLeft:blit(screen, 100, 100)
Screen:flip()
----------------8<----------------

Assumptions
-----------

. Multiple constructors can be defined

As Lua won't support method overloading, I think we should have multiple
constructors.

. Multiple inheritance can be used

Having super_of returning an iterator gives the programmer the choice of
having multiple inheritance or not, I do have in my class system to
mimic Java's interfaces.

. Methods must be called with ':'

Class system implementors must at least agree with this, it'll be
terrible for the programmer to have to remember to use ':' with some
instances and '.' with anothers.

. Properties must be accessed with '.'

Is there another way of doing it? Seriously, maybe all objects should be
opaque, and getter/setter methods provided. But this will increase
typing.

What I like
-----------

. Having classes from various class systems working toghether

What I don't like
-----------------

. The syntax to call constructors

Maybe lhf's token filter can help with that.

. The syntax to call inherited methods

Likewise.

. The repeated typing of 'self'

This has already been discussed, and today I found myself happily typing
'this.foo', 'this.bar' on class constructors of a Java application I'm
working on because I like constructor parameters to match the member
variables. I don't use 'this' in other methods too much though. I just
like I could have the option to use it or not... (no replies necessary).

. This whole class system interoperability thing

This whole thing could be avoided if Lua had it in the core, but I can't
ask for that after asking for the package system to be *out* of the core
:)

Conclusion
----------

It seems to me that class systems interoperability is possible, and I
think the community should agree on some standard (like the one proposed
here) before that package system comes and a lot of packages are made
with incompatible class systems.

Please don't try to check my code, that's quite a long time I don't code
in Lua (finishing my master degree's thesis). Please also forgive me if
I forgot something important, or if the proposal is totally useless...

Regards,

Andre de Leiradella


Reply | Threaded
Open this post in threaded view
|

Re: RES: Standard Object Reflection (was Re: OOP in daydream...)

Petite Abeille

On Jan 19, 2005, at 18:37, André de Leiradella wrote:

. Multiple constructors can be defined

Why have "constructors" in the first place? What about a class methods instead? E.g. anInstance = MyObject:class:new() or such.

Having super_of returning an iterator gives the programmer the choice of
having multiple inheritance or not, I do have in my class system to
mimic Java's interfaces.

What about forgetting about multiple inheritance all together and instead just allow for "behaviors" to be moved around (something along the lines of AOP)? So you could say something like: MyObject:class:addImplementation( anotherClass )?

You could as well support the equivalent of Objective-C categories by providing a function to adds methods to any classes as well: e.g. MyObject:class:addMethod( aMethod ).

. Methods must be called with ':'

Fine.

. Properties must be accessed with '.'

What about forgetting about properties altogether? After all, ivars are implementation details. There is no needs to expose them one way or another.

Is there another way of doing it? Seriously, maybe all objects should be
opaque, and getter/setter methods provided.

I like that better.

 But this will increase typing.

Then everything would be a method:

- Smalltalk: everything is an object.
- Objective-Lua: everything is a method.

. The syntax to call constructors

What about no constructor in the first place?

. The syntax to call inherited methods

What about something like: anInstance:super:doIt()?

. The repeated typing of 'self'

'self' is good. It should be a method as well. After all, OOP is about passing message around. There should always be an explicit target/action pair.

Cheers

--
PA
http://alt.textdrive.com/



Reply | Threaded
Open this post in threaded view
|

Re: RES: Standard Object Reflection

Klaus Ripke
In reply to this post by André de Leiradella
On Wednesday 19 January 2005 18:37, André de Leiradella wrote:
> It seems to me that class systems interoperability is possible, and I
> think the community should agree on some standard
or a bunch of standards to be ready for different requirements

E.g. in Java land, the reflection API (now standard for some years)
and the reflection based beans mechanism is very nice and useful in
some contexts, especially GUI builders (their original app).
OTOH the invention of enterprise beans and their massive misuse in
servlet engines is plain braindead. In a server environment, you'd
want to trade longer startup for faster execution, since it's just a
ridiculous waste of ressources to retrieve the very same information
over and over by convenient but expensive means.


cheers


Reply | Threaded
Open this post in threaded view
|

Re: RES: Standard Object Reflection

Petite Abeille

On Jan 19, 2005, at 18:12, Klaus Ripke wrote:

E.g. in Java land, the reflection API (now standard for some years)

I think that "reflection" (e.g. object "introspection") and a generic object model are two very distinct beasts. An OO system doesn't necessarily implies any publicly available means for introspection. Of course, it's much nicer to have both :)

Cheers

--
PA
http://alt.textdrive.com/


Reply | Threaded
Open this post in threaded view
|

Re: Standard Object Reflection (was Re: OOP in daydream...)

Jay Carlson
In reply to this post by Steve Donovan-2
What about prototype (as opposed to class/instance) object systems?

Reply | Threaded
Open this post in threaded view
|

Re: RES: Standard Object Reflection (was Re: OOP in daydream...)

Mark Hamburg-4
In reply to this post by Petite Abeille
on 1/19/05 9:02 AM, PA at [hidden email] wrote:

>> . The syntax to call inherited methods
> 
> What about something like: anInstance:super:doIt()?

That would only work for one level of inheritance.

Maybe the C++ approach of explicitly naming the parent class has some merit,
though it also has maintenance issues. Those issues could be handle by
defining a local variable named something like "super" at the beginning of a
class declaration.

What gets more interesting is when one builds a multiple inheritance system
that sequences the super classes. Then the next method (as opposed to the
inherited method) depends on the over all chain. For example, given classes
A and B as base classes for multiple-inheritance, it might be interesting to
write:

    function A:method( param )
        call_next_method( self, A, "method", param )
        self.field = param
    end

One could also write an implementation for B that did appropriate chaining
and get different results depending on whether one descended from A and B or
B and A.

Mark


Reply | Threaded
Open this post in threaded view
|

RES: RES: Standard Object Reflection (was Re: OOP in daydream...)

André de Leiradella
In reply to this post by Petite Abeille
>> . Multiple constructors can be defined
> 
> Why have "constructors" in the first place? What about a class methods
> instead? E.g. anInstance = MyObject:class:new() or such.

In one of my class systems I had to "call" the class to get an instance
of it:

screen = Screen(640, 480)

But it allows for only one constructor. The concept of constructor,
although they're just functions at the end, is important because it's
inside the constructor that the new instance is created, and when that
happens you have a lot of housekeeping to do that you don't want to
write in every classes' constructors...

>> Having super_of returning an iterator gives the programmer the
>> choice of having multiple inheritance or not, I do have in my class
>> system to mimic Java's interfaces.
> 
> What about forgetting about multiple inheritance all together and
> instead just allow for "behaviors" to be moved around (something along
> the lines of AOP)? So you could say something like:
> MyObject:class:addImplementation( anotherClass )?

I don't know what behaviors are, could you elaborate?

> You could as well support the equivalent of Objective-C categories by
> providing a function to adds methods to any classes as well: e.g.
> MyObject:class:addMethod( aMethod ).

Things are getting complicated :)

>> . The syntax to call inherited methods
> 
> What about something like: anInstance:super:doIt()?

I think that can work, when a method is looked in a class, it's the
class' responsibility to return it, looking to it's ancestors if
necessary. But I like sugar so I'll wait for lhf's token filter.

Regards,

Andre de Leiradella


Reply | Threaded
Open this post in threaded view
|

RES: Standard Object Reflection (was Re: OOP in daydream...)

André de Leiradella
In reply to this post by Jay Carlson
> What about prototype (as opposed to class/instance) object systems?

Hum, first it was "behaviors", now prototypes. I used to think OOP was
an easy topic.

Regards,

Andre de Leiradella


Reply | Threaded
Open this post in threaded view
|

RES: RES: Standard Object Reflection (was Re: OOP in daydream...)

André de Leiradella
In reply to this post by Mark Hamburg-4
> on 1/19/05 9:02 AM, PA at [hidden email] wrote:
> 
>>> . The syntax to call inherited methods
>> 
>> What about something like: anInstance:super:doIt()?
> 
> That would only work for one level of inheritance.

That depends on what super does inside its metamethods. It can search
for the method inside itself and, if not found, search recursively in
it's ancestors.

> Maybe the C++ approach of explicitly naming the parent class has some
> merit, though it also has maintenance issues. Those issues could be
> handle by defining a local variable named something like "super" at
> the beginning of a class declaration.   

Not sure I'm following you here. This local variable "super" defined at
the beginning of a class solves the problem you described for C++,
right?

> What gets more interesting is when one builds a multiple inheritance
> system that sequences the super classes. Then the next method (as
> opposed to the inherited method) depends on the over all chain. For
> example, given classes A and B as base classes for
> multiple-inheritance, it might be interesting to write:   
> 
>     function A:method( param )
>         call_next_method( self, A, "method", param )
>         self.field = param
>     end
> 
> One could also write an implementation for B that did appropriate
> chaining and get different results depending on whether one descended
> from A and B or B and A.  
> 
> Mark

The order in which inherited methods are searched for will depend on the
implementations of particular class systems and changing it may break
things.

Regards,

Andre de Leiradella


Reply | Threaded
Open this post in threaded view
|

Re: Standard Object Reflection (was Re: OOP in daydream...)

Petite Abeille
In reply to this post by André de Leiradella

On Jan 20, 2005, at 04:48, André de Leiradella wrote:

Hum, first it was "behaviors", now prototypes. I used to think OOP was
an easy topic.

Appearances are deceptive :o)

"why Smalltalk never caught on"

http://hoho.dyndns.org/~holger/smalltalk.html

Cheers

--
PA
http://alt.textdrive.com/



Reply | Threaded
Open this post in threaded view
|

Re: RES: Standard Object Reflection

Klaus Ripke
In reply to this post by Petite Abeille
On Wednesday 19 January 2005 18:34, PA wrote:
> I think that "reflection" (e.g. object "introspection") and a generic
> object model are two very distinct beasts.
absolutely!

Since reflection is mostly used in environments which are rather bloated
anyway, IMHO it would be ok to use rather expensive and semi-external
means like debug extensions -- no need to clutter a generic object model
just to enable reflection.


cheers


Reply | Threaded
Open this post in threaded view
|

Re: RES: Standard Object Reflection

Steve Donovan-2
In reply to this post by Klaus Ripke
>>> [hidden email] 01/20/05 01:37PM >>>
>Since reflection is mostly used in environments which are rather
bloated
>anyway, IMHO it would be ok to use rather expensive and semi-external
>means like debug extensions -- no need to clutter a generic object
model
>just to enable reflection.

Fair enough - it doesn't need to be performance-critical.

But if I'm a class browser and there's no
convention, then I'm reduced to looking inside every
table to see if they contain functions, or have
metatables.  Could be done, will be expensive;  
probably not possible to deduce inheritance
relationships.

steve d.


Reply | Threaded
Open this post in threaded view
|

Re: RES: RES: Standard Object Reflection (was Re: OOP in daydream...)

Mark Hamburg-4
In reply to this post by André de Leiradella
on 1/19/05 7:52 PM, André de Leiradella at [hidden email] wrote:

>> on 1/19/05 9:02 AM, PA at [hidden email] wrote:
>> 
>>>> . The syntax to call inherited methods
>>> 
>>> What about something like: anInstance:super:doIt()?
>> 
>> That would only work for one level of inheritance.
> 
> That depends on what super does inside its metamethods. It can search
> for the method inside itself and, if not found, search recursively in
> it's ancestors.

The issue I was raising was if A subclasses B subclasses C and each provides
an implementation of doIt, then you aren't passing enough information to
super for it to do it what it needs. One essentially needs two pointers:

* The instance object
* The current point in the inheritance hierarchy

> 
>> Maybe the C++ approach of explicitly naming the parent class has some
>> merit, though it also has maintenance issues. Those issues could be
>> handle by defining a local variable named something like "super" at
>> the beginning of a class declaration.
> 
> Not sure I'm following you here. This local variable "super" defined at
> the beginning of a class solves the problem you described for C++,
> right?

The super variable had to do with Lua though you can achieve some of the
same effects in C++ with a macro or a typedef.

Let's do a Lua version with Base and Derived and some simple syntax along
the lines of what's been proposed so far (though I make a distinction
between instance methods and class methods).

    Base = class()  -- no other base class

    function Base.methods:doIt()
        -- do something
    end

    Derived = class( Base )

    function Derived.methods:doIt()
        -- call the base method: How?
        -- do some more work
    end

How do we call the base method from the derived method? One approach is:

    function Derived.methods:doIt()
        Base.methods.doIt( self )
        -- do some more work
    end

This is the C++ approach. It's chief downfall comes if you change the
inheritance hierarchy and forget to update the places that call through. You
can mitigate that with a local variable:

    Base = class()  -- no other base class

    function Base.methods:doIt()
        -- do something
    end

    Derived = class( Base )

    local super = Base

    function Derived.methods:doIt()
        super.methods.doIt( self )
        -- do some more work
    end

You could actually cache the methods table in a local rather than the class
in this example.

If classes know their superclasses, we could also write:

    function Derived.methods:doIt()
        Derived.superclass.methods.doIt( self )
        -- do some more work
    end

With a certain amount of trickery and conventions, one might be able to
implement something like:

    function Derived.methods:doIt()
        self:super( Derived ):doIt()
        -- do some more work
    end

This, however, requires that super construct a proxy object which will have
special versions of the methods that will get the base instance and pass
that to the actual methods.

Finally, note that these sort of issues also exist in prototype-based and
delegation-based systems. We want to know both the object-of-entry into the
method chain together with our current point in that chain.

Mark



Reply | Threaded
Open this post in threaded view
|

Moving towards a class system package proposal

André de Leiradella
> The issue I was raising was if A subclasses B subclasses C and each
> provides an implementation of doIt, then you aren't passing enough
> information to super for it to do it what it needs. One essentially
> needs two pointers:   
> 
> * The instance object

Isn't it "self"? Every method will get it when called with ":".

> * The current point in the inheritance hierarchy

In my class system, this is a _CLASS field found on every instance that
points to their respective classes, which in turn hold their methods and
have a _SUPER field.

> Let's do a Lua version with Base and Derived and some simple syntax
> along the lines of what's been proposed so far (though I make a
> distinction between instance methods and class methods).  
> 
>     Base = class()  -- no other base class

Shouldn't you create the methods table, i.e. Base.methods = {}?

> 
>     function Base.methods:doIt()
>         -- do something
>     end
> 
>     Derived = class( Base )
> 
>     function Derived.methods:doIt()
>         -- call the base method: How?
>         -- do some more work
>     end
> 
> How do we call the base method from the derived method? One approach
> is: 
> 
>     function Derived.methods:doIt()
>         Base.methods.doIt( self )
>         -- do some more work
>     end
> 
> This is the C++ approach. It's chief downfall comes if you change the
> inheritance hierarchy and forget to update the places that call
> through. You can mitigate that with a local variable:  
> 
>     Base = class()  -- no other base class
> 
>     function Base.methods:doIt()
>         -- do something
>     end
> 
>     Derived = class( Base )
> 
>     local super = Base
> 
>     function Derived.methods:doIt()
>         super.methods.doIt( self )
>         -- do some more work
>     end
> 
> You could actually cache the methods table in a local rather than the
> class in this example. 
> 
> If classes know their superclasses, we could also write:
> 
>     function Derived.methods:doIt()
>         Derived.superclass.methods.doIt( self )
>         -- do some more work
>     end
> 
> With a certain amount of trickery and conventions, one might be able
> to implement something like: 
> 
>     function Derived.methods:doIt()
>         self:super( Derived ):doIt()
>         -- do some more work
>     end
> 
> This, however, requires that super construct a proxy object which
> will have special versions of the methods that will get the base
> instance and pass that to the actual methods.  
> 
> Finally, note that these sort of issues also exist in prototype-based
> and delegation-based systems. We want to know both the
> object-of-entry into the method chain together with our current point
> in that chain.   

Those would be "self" and "self._CLASS".

I see you store methods inside a field within the class table, I prefer
to hide that implementation characteristic from users. If we want to
have class systems which are interoperable I think we should agree on a
set of standards before moving on. This is what I would like to have:

1) Defining a new class

NewClass = class([SuperClass] {, SuperClass})

It's not strictly necessary to have it standardized, if you're writing a
Lua app that uses more than one class system, you can elect on of them
and use its syntax to create new classes, but it's good to have just one
way of doing it no matter which class system you're using.

2) Defining the constructor

function Class:new([argument], {, argument})

The name of the constructor, "new" in this case, should be standardized.
Constructor overloading would be accomplished by examining the number of
arguments and their types.

3) Defining a method

function NewClass:blit([argument] {, argument})

4) Creating an instance of a class

instance = Class([argment] {, argument})

Like item 1, we don't have to have it standardized, but again it's good
to have only one way to create instances.

The constructor "new" would be called with a new instance of the class
and all arguments passed to Class, to avoid having to create the
instance inside the constructor and initializing it's "hidden" fields
that make the class system work.

5) Calling methods

instance:method([argument] {, argument})

Methods should always be called with ":", having mixed ways of calling
methods isn't a good thing.

6) Calling inherited methods

instance:super.method([argument] {, argument})

I don't see why calling inherited methods from super classes other than
the direct super class is needed.

7) Accessing properties

instance.property

I think that those standards plus the class system package proposed by
Steve Donovan and augmented by me would allow completely, interoperable
class systems, and no one would be forced to use just one class system
throughout the entire application. But I may be wrong :)

Regards,

Andre de Leiradella


Reply | Threaded
Open this post in threaded view
|

Re: Moving towards a class system package proposal

Mark Hamburg-4
on 1/22/05 11:26 AM, André de Leiradella at [hidden email] wrote:

>> The issue I was raising was if A subclasses B subclasses C and each
>> provides an implementation of doIt, then you aren't passing enough
>> information to super for it to do it what it needs. One essentially
>> needs two pointers:
>> 
>> * The instance object
> 
> Isn't it "self"? Every method will get it when called with ":".
> 
>> * The current point in the inheritance hierarchy
> 
> In my class system, this is a _CLASS field found on every instance that
> points to their respective classes, which in turn hold their methods and
> have a _SUPER field.

We can ask self for it's class. We can ask a class for it's superclass, but
you need a way to ask the question: For the method implementation currently
executing what is the implementation in the next class up the inheritance
chain. That means that a method needs to know not just self, but the class
it is implemented in or the superclass of that class.

In my example above, for example, given just self, how would the
implementation of a method in B, find the implementation in C? It either
needs to know that it is part of B or it needs to know that it's superclass
is C.

> 
>> Let's do a Lua version with Base and Derived and some simple syntax
>> along the lines of what's been proposed so far (though I make a
>> distinction between instance methods and class methods).
>> 
>>     Base = class()  -- no other base class
> 
> Shouldn't you create the methods table, i.e. Base.methods = {}?

I assumed that the class() function would do that.

>> Finally, note that these sort of issues also exist in prototype-based
>> and delegation-based systems. We want to know both the
>> object-of-entry into the method chain together with our current point
>> in that chain.  
> 
> Those would be "self" and "self._CLASS".

That only gets you one level. What happens when the method inherited from a
superclass wants to call the method above it?

> 6) Calling inherited methods
> 
> instance:super.method([argument] {, argument})
> 
> I don't see why calling inherited methods from super classes other than
> the direct super class is needed.

The syntax you present isn't legitimate Lua, but I'll ignore that for now.

I haven't been worrying about how one jumps over superclasses in the
inheritance chain. The issue is that one wants self to remain the same
throughout the process so that it can be passed elsewhere and so that we can
call overridden methods.

Returning to the multi-level inheritance hierarchy with implementations for
the same method at each level (using your notation):

    Base = class()
    Middle = class( Base )
    Leaf = class( Derived )

Furthermore, assume we have a method named "method" that we want to
implement at each level and at each level it needs to call the inherited
version.

    function Base:method()
        print "Base method, nothing else to do"
    end

    function Middle:method()
        print "Middle method, calling Base method..."
        -- call the version in Base
    end

    function Leaf:method()
        print "Leaf method, calling Middle method..."
        -- call the version in Middle
    end

The problem is to write the code for the two commments.

The "C++ solution" is to explicitly name the superclass in the call.

You seem not to want to do that. The problem is that when we call to
Middle:method the value of self is the same as it was in Leaf:method. So, an
expression only involving self doesn't have enough information to result in
a call to Base:method instead of another call to Middle:method. Remember
self._CLASS will always be Leaf.

Languages that provide "super" as a keyword take steps in the compiler to
give it meaning. For example, Objective-C performs super class calls via:

    id objc_msgSendSuper(
            struct objc_super* superContext,
            SEL theSelector,
            ...)

where objc_super is defined as "including the instance of the class that is
to receive the message and the superclass at which to start searching for
the method implementation".

Mark



Reply | Threaded
Open this post in threaded view
|

Re: Moving towards a class system package proposal

Petite Abeille

On Jan 22, 2005, at 21:30, Mark Hamburg wrote:

where objc_super is defined as "including the instance of the class that is to receive the message and the superclass at which to start searching for
the method implementation".

Still waiting for my Lua book to show up... so feel free to slap me on the head if I'm talking nonsense :)

Looking at Chapter 16.2 - Inheritance [1] in Programming in Lua, Roberto defines its constructor method like this:

    function Account:new (o)
      o = o or {}
      setmetatable(o, self)
      self.__index = self
      return o
    end

What about redefining it along the following lines for the Lua root class, called "LUObject" for the sake of argument:

    function LUObject:new ( anInstance )
      anInstance.super = self
      setmetatable( anInstance, self)
      self.__index = self
      return anInstance
    end

(Not quite sure about the exact syntax and parameters order here, but hopefully you get the gist of it :)

When subclassing LUObject, one calls the super class "new" factory method, passing itself as an instance:

    function MySubclass:new ()
      return LuaObject( self )
    end

Now there is an explicit class hierarchy between the two objects.

Assuming an existing "description" method in the superclass one would like to extend, one could now invoke it like this:

    function MySubclass:description ()
      super:description()

      print "my very own description as well"
    end

Does this make any kind of sense?!?

Thanks!

Cheers

--
PA
http://alt.textdrive.com/


[1] http://www.lua.org/pil/16.2.html


Reply | Threaded
Open this post in threaded view
|

Re: Standard Object Reflection (was Re: OOP in daydream...)

Ivan Kolev-2
In reply to this post by Steve Donovan-2
I once had the idea to create a standard way of describing values in Lua. The purpose was to automatically validate values at runtime (mostly function parameters and return values), as well as provide built-in documentation. I imagined that a module should come with documentation and contract (as in "design by contract"). A possible GUI tool (like an IDE) could load a module and diplay in a tree view all of the types (classes) and functions it exposes, along with their contract and documentation. The formal description of the contract (e.g. allowed ranges for numerical values, regexp patterns for string values, is nil accepted or not, etc.) allows for easy automatic validation at runtime.

I wrote some Lua code about that and it worked OK. Here's how a descriptor for a module looked like:

-- first comes the module code

...

-- at the end come the descriptors

public.desc = { types = {}, interface = {} }

public.desc.docs = [[
General module documentation comes here...
]]

public.desc.types.testResults = desc
{
"A table describing the current state of the tests",   -- short description
"table",         -- type or list of possible types
content =        -- for tables - descriptors of the content
{
  nTests = desc { "Total number of tests", "int", min = 0 },
  nOK = desc { "Number of successful tests", "int", min = 0 },
  nFailed = desc { "Number of failed tests", "int", min = 0 },
  log = desc { "Log of the test results", "string" },
  coverage = desc { "Table of line coverage", "table" },
}
docs = [[
Detailed documentation comes here...
]]
}

public.desc.interface.testModule = desc
{
"Loads a whole module and runs the tests it contains, which must be contained in the __tests field",

public.testModule,          -- the function itself

returns =                   -- function return value(s) descriptors
{
  desc { "True if the test run was successful", "boolean" },
  desc { "Error message if the test run failed, or a table of test results",
         { "string", public.desc.types.testResults }
       },
},

params =                    -- function parameter descriptors
{
  desc { "Module filename", "string" },
  desc { "Output filename for the coverage HTML file", "string", allowNil = true }
}

docs = [[
Detailed documentation comes here...
]]
}

The descriptor module provided a validator function which could be set as a call hook and validate the parameters and return values for each function call (of course, that can be done only in testing mode, if speed is important). The examples are from the simple testing module I wrote, which is the natural extension of the descriptors module - it simply loads a given module and runs the list of tests it contains (with the validator function enabled).

I don't have time for Lua at the moment (since I finally decided against using it for the things I'm doing at the moment - C++ is still the best tool), so I'm just throwing the idea... I think Lua's convenient and powerful data description abilities can (and should) be used for detailed reflection/introspection.

Regards,
Ivan


Reply | Threaded
Open this post in threaded view
|

Re: Moving towards a class system package proposal

Mark Hamburg-4
In reply to this post by Petite Abeille
on 1/22/05 2:02 PM, PA at [hidden email] wrote:

> Assuming an existing "description" method in the superclass one would
> like to extend, one could now invoke it like this:
> 
>    function MySubclass:description ()
>      super:description()
> 
>      print "my very own description as well"
>    end
> 
> Does this make any kind of sense?!?

Only if super is a local variable equal to the superclass. That's basically
the C++ solution -- i.e., explicitly specify the superclass in the upcall.

For example:

    MySubclass = class( MyBaseClass )
    local super = MyBaseClass

Now, I can feel free to write methods in MySubclass that reference super and
hence reference MyBaseClass provided that I write them in a context where
this definition of super is visible.

Of course, I could also just write them to explicitly mention MyBaseClass.
That becomes a maintenance issue, however, if I change the class hierarchy.

(I can do this in C++ as well by putting a typedef for super at the top of
each class.)

My point was that the value of self was insufficient on its own to determine
the point in the class chain to start looking when making a call to the
inherited version of a method because self always starts at the end of the
chain and we would need to know how far we'd gotten in recursing through the
calls to inherited versions of the method.

Mark

P.S. Lua environment skankery (not actually recommended): Classes could
define a custom environment that they assign to each of their methods that
would include a definition of super.



12