[Zope3-dev] Interface declaration API
Jim Fulton
jim@zope.com
Tue, 11 Mar 2003 16:35:02 -0500
Phillip J. Eby wrote:
> At 02:46 PM 3/11/03 -0500, Jim Fulton wrote:
>
>> Subclassing is done to reuse implementation. If interfaces were
>> inherited, then you would be unable to subclass a class without
>> also promising to implement the base class interfaces. I've always
>> viewed this as an undesireable requirement, but maybe I'm wrong,
>> since so many people desire it, ;) So I'm willing to give on this.
>
>
> Ironically, you then go on to say that 'doesNotImplement()' would be a
> YAGNI. :) I actually suggested 'doesNotImplement()' as a way to deal
> with the situation above. If one wishes to reject base class
> interfaces, one might then do so explicitly.
The way you would reject base-class interfaces is to use implements. That is,
if you use implements, you implement *just* what you say you implement,
so I still think 'doesNotImplement()' is a yagni.
>
>
>> Soooooooo ....
>>
>> Let's suppose we decided that in:
>>
>> class C(A, B):
>>
>> alsoImplements(IFoo)
>>
>> The outcome would be that C's instances would implement
>> IFoo and whatever A's and B's instances implement.
>>
>> This is implementable, but with a hack. The hack is that the
>> descriptor needs to update itself the first time that it's used. :(
>>
>> In::
>>
>>
>> class C(A, B):
>>
>> pass
>>
>> The outcome would be that C's instances would implement
>> whatever A's and B's instances implement.
>>
>> This too requires a pretty heavy hack. The first time you tried to
>> get the instance interface spec for a C, you'd inherit a spec from
>> A or B. The inherited descriptor would detect that it was inherited
>> and create a new descriptor for C, merging the interfaces from A and
>> B.
>>
>> Finally, with:
>>
>> class C(A, B):
>>
>> implements(IFoo)
>>
>> The outcome would be that C's instances would implement
>> only IFoo.
>
>
> I've previously implemented late-binding class descriptors of this kind,
> and I agree that they aren't appropriate for Zope 3. One potential
> issue, for example, is thread-safety of the object that does the late
> binding (because non-persistent classes are normally shared between
> threads).
>
> I do notice, however, that your description seems to imply a
> descriptor-based implementation.
That's what I was thinking of. Hm. I'll have to ponder a non-descriptor
implementation. It might make things cleaner if we decide change the inheritence
semantics. I was thinking in terms of descriptors because they avoid having
to sniff to decide if something is a class, which is *really really* painful
in the presense of security proxies. I guess we shouldn't get too hung up
on implementation difficulties at this point and focus on what we want.
> If the intention is to encapsulate the
> implementation, it's not clear to me that putting the behavior in a
> descriptor is the most useful way to go. For example, 'implementedBy()'
> might look something like this:
>
> def implementedBy(klass):
>
> spec = klass.__dict__.get('__implements__')
>
> if spec is None:
> spec = klass.__implements__ = InterfaceSpecification(
> *tuple([implementedBy(c) for c in klass.__bases__])
> )
>
> return spec
where this is a non-descriptor-based approach.
> Notice this also has the same thread-safety issue.
Yes.
> Also, you have to
> have *something* that does this, if you want 'implementedBy()' to return
> a mutable object!
Is your point that you have to mutate the class if you want 'implementedBy()'
to return a mutable object? You are right, although you could arrange
for the mutation to be defered until you actually modified the spec, which
wouldn't be so bad.
> So, if you really want to simplify this, I'd suggest going back to
> immutable interface specs.
I don't see how that simplified anything. Is your point that you want to
avoid having a query method modify the thing being queried?
> This would then require an explicit
> rebinding (e.g. klass.__implements__ += IFoo)
That would be unacceptable, as it would expose the declaration implementation,
which we want to avoid.
> and wouldn't need a check
> for whether the specs were inherited or not. You also wouldn't need a
> descriptor, which means that "classic" classes might be better
> supported. (I'm not positive that any of classic classes' limitations
> on descriptors are an issue here, though.)
Is your objection that:
getImplemented(cls)
could mutate the class?
What if it didn't, but:
getImplemented(cls).set(I)
did?
> In order to preserve encapsulation, you would need to have functions
> like 'addImplements()', 'addProvides()', 'setImplements()', etc. While
> this expands the number of API functions, it also increases the
> explicitness of such operations (one may grep for 'setImplements()' more
> easily than for 'foo=implementedBy(Bar) ... multiple lines of code ...
> foo.add(IBaz)'. That is, one is now required to put the operation in
> the same place with the thing being operated upon.
>
> Doing this would actually give you an implementation benefit to sticking
> with "first in MRO" inheritance semantics for interface declarations,
> er, specifications.
how so?
> (By the way... I always stumble on calling them "interface
> specifications". To me, the interface specification is the "class
> IFoo(Interface):" block, while the "__implements__ = IFoo" is an
> *interface declaration*. But I may be alone in this.)
I see your point.
> Anyway... what I'm saying is, if you stick with mutable specifications,
> you might as well implement "merged __bases__" inheritance, because
> there is no less implementation complexity.
Because you have to mutate classes in either case?
> AFAICT, to reduce the
> implementation complexity from what you described, you have to also make
> specifications immutable, so that it never matters if the specification
> is inherited or not. Otherwise, you are right back in the same boat of
> needing to either have a lazily-binding descriptor, or a function that
> acts like one "from the outside".
Even if they were immutable, you'd want to manipulate the descriptor and/or the class
for efficiency sake if you don't want the current inheritence semantics.
Jim
--
Jim Fulton mailto:jim@zope.com Python Powered!
CTO (888) 344-4332 http://www.python.org
Zope Corporation http://www.zope.com http://www.zope.org