[Python-Dev] type categories

Jeremy Hylton jeremy@alum.mit.edu
Fri, 23 Aug 2002 22:52:53 -0400


>>>>> "GvR" == Guido van Rossum <guido@python.org> writes:

  >> This discussion appears about over, but I haven't seen any
  >> solutions via inheritance.  Other languages that lack true
  >> interfaces use abstract base classes.  Python supports multiple
  >> inheritance.  Isn't this enough?

  GvR> I haven't given up the hope that inheritance and interfaces
  GvR> could use the same mechanisms.  But Jim Fulton, based on years
  GvR> of experience in Zope, claims they really should be different.
  GvR> I wish I understood why he thinks so.

Here's a go at explaining why the mechanisms need to be separate.  I'm
loathe to channel Jim, but I think he'd agree.

We'd like to use interfaces to make fairly strong claims.  If a class
A implements an interface I, then we should be able to use an instance
of A anywhere that an I is needed.  This is just the straightforward
notion of substitutability.  I'm saying this is a strong claim because 
we want an A to behave like an I.  By behave, I mean that the
interface I can describe behavior beyond just a method name or
signature.

Why can't we use the current inheritance mechanism to implement the
interface concept?  Because the inheritance mechanism is too general.
If we take the class A, anyone can create a subclass of it, regardless
of whether that subclass implements I.

Say you wanted to write LBYL code that tests whether an object
implements an interface.  If you use a marker class and isinstance()
for the test, the inheritance rules makes it impossible to express
some relationships.  In particular, it is impossible to write a class
B that inherits from A, but does not implement I.  Since our test is
isinstance(), any subclass of A will appear to implement I.  This is
unfortunate, because inheritance is a great implementation trick that
shouldn't have anything to do with the interface.

If we think about it briefly in terms of types.  (Python doesn't have
the explicit types, but sometimes we reason about programs as if they
did.)  Strongly typed OO languages have to deal in some way with
subclasses that are not subtypes.  Some type systems require
covariance or contravariance or invariance.  In some cases, you can
write a class that is a subclass but is not a subtype.  The latter is
what we're hoping to achieve with interfaces.

If we imagined an interface statement that was explicit and no
inherited, then we'd be okay.

class A(SomeBase):
    implements I

class B(A):
    implements J

Now we've got a class A that implements I and a subclass B that
implements J.  The test isinstance(B(), A) is true, but the test
implements(B(), I) is not.  It's quite helpful to have the
implements() predicate which uses a rule different from isinstance().

If we don't have the separate interface concept, the language just
isn't as expressive.  We would have to establish a convention to
sacrifice one of -- a) being able to inherit from a class just for
implementation purposes or b) being able to reason about interfaces
using isinstance().  a) is error prone, because the language wouldn't
prevent anyone from making the mistake.  b) is unfortunate, because
we'd have interfaces but no formal way to reason about them.

Jeremy