Repository navigation
Polymorphism doesn't work #62
Description
In Specl, I have a Process object that is produced by running a specl.shell subprocess, and which in turn has a selection of special matchers (.to_exit (0), .to_output "stdout", .to_fail_with "stderr", etc) which check that they have received a process object.
In Zile, I have an Editor object, which is a clone of specl.shells Process produced by running some zlisp code in a zemacs subprocess, but with additional fields for buffer and minibuffer contents upon completion. I'd have matchers that are specific to an Editor (.to_match_minibuf "C-x C-g is undefined", etc) which check they have received an Editor object. But, the specl.shell Process matchers would still be useful, and Editor is derived from Process so they would work as expected, if not for the fact that they reject non-Process objects.
I have some options for how to provide this:
- Derived prototype objects that need to be accepted by functions designed for parent objects, must not set a new
_typename. This is what I'm doing in Zile right now...prototype (Editor {})returns"Process". The problem with this approach is that I can also pass rawProcessobjects to theEditorapis that require the extra fields, but not find out about it until the interpreter crashes with anilvalued field access. This is clearly not scalable. std.Object._typecan be a list, and setting_typein the table argument to object cloning appends that name onto the cloned object's_typelist rather than overwriting it.Object.prototypereturns the top value of that stack, and we add a newObject.isamethod to check the whole list to see whether an object has the named type in its type list. This will solve my current issue, and adds a sort of single inheritance feature tostd.objectwithout much development work.- Write an
Interfaceapi. AnInterfaceis just an object that associates a name (Iterable,Indexable, etc) with a list of method calls that an object that provides the named interface must respond to. Interface methods must be added to an object first, and then are declared (added to an_interfacesset in the metatable of the object) with an api that checks the required methods calls are supported. Then at runtime, instead of rejecting anything that's not aProcessobject, I can accept anything that supports theProcessinterface, and bothProcessobjects and the derivedEditorobjects will be accepted and will work properly, and theEditoronly apis can detect accidentalProcessobjects before the nil access errors.
I'm leaning towards (3) because it sidesteps any horrors with trying to support a multiple inheritance paradigm, and yet still provides all the advantages of multiple inheritance. And it's clearly much more powerful and useful than the other two options. It seems like the runtime overhead will be incredibly light, except at the point of checking that declared interfaces are properly supported by an object, which in turn can be mitigated by skipping those checks when _DEBUG == false -- or not implemented at all I suppose, though I quite like the safety of preventing declaration of interfaces that are not conformed to.
My worry is that (3) is over-engineering a solution, somewhat tempered by not wanting to make a release with support for (2) and then wanting to change my mind to (3) in a later release when (for example) Zile starts to have a proper object model in its implementation but (2) starts getting in the way of doing things elegantly.
Activity
rrthomas commentedon Jun 7, 2014
Interfaces is the bunny. My original objects didn't check their argument types, so objects sort of were interfaces too.
gvvaughan commentedon Jun 8, 2014
Awesome. If you think explicit Interface objects will be useful too, then I have more confidence that I'm not going down a rabbit hole, I'll work on this after the next release then :-)
gvvaughan commentedon Jun 8, 2014
Oh wait... Or is your reference to bunnies a hint that I am in danger of falling down a rabbit hole? At first I thought it was just a colloquial superlative I hadn't seen before!
rrthomas commentedon Jun 8, 2014
Nope, it's good! "That's the bunny" may well be idiolect; I'll stick to "That's the badger" in future.
gvvaughan commentedon Jun 14, 2014
Excellent, I'll forge ahead in due course then :)
Is that a honey-badger or a moon-badger, btw?
rrthomas commentedon Jun 14, 2014
It's a badgerbadgerbadger.com.
On 14 June 2014 06:29, Gary V. Vaughan notifications@github.com wrote:
http://rrt.sc3d.org
gvvaughan commentedon Aug 2, 2014
Note that I closed #68 before moving
std.object.prototypetostd.type.The core problem of figuring out what type something is, especially being able to recognize primitive tables even when they are objects seems like it would be better solved by polymorphism. As long as we make every
objectrespond in the affirmative tothingy:is_a "table"that problem is solved.This all implies that we'll be maintaining two sets in each object's metatable:
_typeis the set of objects leading back totablein the inheritance tree;_interfaceis the set of interfaces the object implements;So we can see whether a particular object was somehow derived from another using:
...and complain about passing non-conforming objects using: