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

Re: [Tads3] 'inherited' and propNotDefined



Kevin Forchione wrote:
> 
> That seems right to me as well. Because you've defined xyz(x) on the object,
> so propNotDefined() shouldn't catch it on that object.

I'm still not convinced by this reasoning, but the fact that most
folks here seem to expect this behavior is certainly an indication
that it's probably the right way to go anyway.

> If that were the case, the question then involves nested inherited() and
> where the capture would take place. If, for instance, we have the following:
> 
> class A: object
>   propNotDefined(prop, [args]) //...
> ;
> class B: A
>   propNotInherited(prop, [args]) //...
>   xyz(x) { return inherited(x); }
> ;
> class C: B
>   propNotInherited(prop, [args]) //...
>   xyz(x) { return inherited(x); }
> ;
> 
> Now, which propNotInherited() should catch the fact that class B doesn't
> inherit xyz(x)? If class B defines propNotInherited() then perhaps that
> method should catch the call, but if class B *didn't* define
> propNotInherited() then perhaps class C's propNotInherited() should catch
> the call.
> 
> In a sense, I'm picturing propNotInherited() as working up the inheritance
> tree in a way analogous to how a throw would work its way up the call chain,
> with the exception that the chain would terminate at the class that
> initiated the first inherited().

This reverse-inheritance behavior strikes me as rather perverse.  I
suspect it's not actually implementable in general, either, given the
complex inheritance relationships possible.  So, would this behavior
actually be justifiable?  So far, the justification for *some* kind of
undefined property handling seems to be insulation from a certain
limited class of library changes.  Your scenario involves a library
with A, an extension with B, and yet another body of code with C.  You
want to allow C to be robust with respect to certain changes (method
removals which turn out not to remove vital functionality if caught by
C's general handler) in A, even though B is not robust to such
changes.  That's rather obscure.  It also assumes that if
propNotInherited *is* defined on both B and C, B's should take
precedence, which is questionable.


I have another question about all this: how do delegated calls fit in
to all of this?  We've got propNotDefined for ordinary calls.  We have
propNotInherited proposed for inherited calls.  Would a third form be
necessary?  That starts getting rather messy, doesn't it?  I'm
definitely starting to think that sticking with a singe propNotDefined
is going to be easiest to work with.

Actually, I believe this consideration has given me a fairly good
concrete example supporting the argument that propNotDefined lookup
should begin on the target object (superclass for inherited, delegate
for delegated).  During the actor state discussion, we discussed
shadowing proxies which would delegate undefined methods to the
underlying actor object instead of passing control to it completely.
Perhaps something like this:

  class ShadowProxy: object
    target = nil
    propNotDefined(prop, [args])
    {
      return delegated target.(prop)(args...);
    }
  ;

Now, for this to be widely useful, we'd need some way to intercept
property assignments.  We don't have these, and maybe we never will.
That's why this is only a "fairly good" example and not an absolutely
convincing practical example.  But it still provides a more concrete
case to think about.  With that in mind, suppose we have an object
that uses propNotDefined for some other purpose.  For concreteness,
let's say error reporting.

  a: object
    propNotDefined(prop, [args]) { "prop not defined: <<prop>>" }
  ;

  p: ShadowProxy
    target = a
  ;

If we call p.xyz(), we get p.propNotDefined(&xyz), which delegates to
a.xyz().  When we find that missing, lookup of propNotDefined must
proceed from a, not p, for correct behavior.  For consistency,
inherited lookups should proceed the same way, from the target.

Of course, that all assumes a single unified propNotDefined handler.
That does place some restrictions on flexibility of usage, but it has
the advantage of being clean, simple, and consistent.

-Jesse