PEP 245

Alex Martelli aleaxit at yahoo.com
Tue Apr 3 06:23:40 EDT 2001


"Alex Shindich" <shindich at itginc.com> wrote in message
news:mailman.986255463.27488.python-list at python.org...
> >in C++, one normally uses _private_ inheritance for this specific purpose
> There is nothing private in Python. There is "__" name mangling of course,
> but that is it.

Exactly.  In particular, '__' name mangling does not apply to inheritance.


> But your point is well taken. Code reuse should not be mixed with class's
> interfaces. Still, in C++ multiple inheritance is used as the simplest for
> of interface implementation, and abstract classes with only virtual
methods
> can be treated as interfaces.

Yes: C++ sort-of-confuses "I implement an interface" and "I conveniently
use [some part of] a class in my own implementation" under the single
rubric of 'inheritance' -- pretty bad in itself, and the distinction
between public and private inheritance is a helpful band-aid given this
confusion.  Better, of course, not to have the confusion at all.  In
Python, we also lack the band-aid, so it's _particularly_ important to
avoid confusing these two concepts -- both important, but quite distinct.


> >COM, per se, has no concept of 'compile time' either.  Among the
languages
> >best supporting COM are some (such as Python:-) which do very little, if
> >anything, "at compile time".
> OK. That is partially true. If a COM component is implemented using
multiple
> inheretence, then C++ compiler will force you to implement all the
interface

COM does not mandate that a C++ compiler is used, nor does it have
anything to say regarding HOW such a compiler (or one for another
language, such as Eiffel) be used to implement COM objects -- as
long as certain binary layouts are obtained, and various semantics
rules are respected, COM couldn't care less about HOW this is
brought about, nor WHEN (COM has *NO* concept of "compile time").

> methods. If agregation, composition, or flyweights are used, then there is
> no compile time safety.

You seem to be labouring under the misconception that "COM" means
"some specific COM implementation I have in mind, based on a certain
specific usage of some kind of C++ compiler".  This is simply not
the case.  You were talking about confusion being engendered by
interfaces bereft of compile-time checks among programmers who are
"familiar with COM".  I insist that, if such programmers ARE indeed
well-versed in COM itself, lack of compile-time checking is absolutely
nothing new for them.  People who wrongly _believe_ they understand
COM (perhaps because they have let themselves be guided by 'wizards'
to effect one possible COM implementation strategy out of the many
zillions that are feasible [and potentially important]) may possibly
have one little extra nugget of confusion added to the considerable
amount thereof they already labour under, but in any case they are
already sufficiently befuddled that the only viable strategy is to
help them clear up their original confusion -- there isn't much that
Python, per se, can do about that.


> >In Python's case, we don't share the needs and design constraints which
> >led to COM's rules (proxying, marshaling...), so there is no need to
> >impose identity-constraints and the resulting complications (even today,
> >Python allows an object to keep identity BUT change behavior, e.g. by
> >changing its __class__).  But that has really nothing to do with
> >compile-time vs run-time.
> I see identity constraints as philosophical issue.

There are more things in heaven and earth, Mr Shindich,
Than are dreamt of in your philosophy.

_I_ (and the designers of COM) see identity constraints as highly
pragmatical issues that ensure certain proxying and marshaling
strategies (and, more generally, certain client-side choices for
access and usage) will work -- constraints on object implementers
for the benefit of optimizations in the COM infrastructure (and,
to a lesser degree of importance, for the benefit of client-side
coders; e.g., the "reflexivity" of QI ensures client-code can
choose to hold a single pointer to a certain interface on a given
object, and will always be able to QI back to other interfaces at
need, rather than needing to use some specific 'privileged itf'
for such purposes).  Different sets of constraints might also work,
enabling different strategies from client-side, implementer-side,
and infrastructure, with different performance tradeoffs, but it
is important to have SOME set of very precise ground-rules for
"object identity", so that client-code, implementation-code, and
infrastructure-code, each written in very diverse languages, can
all work together in harmony (I happen to believe that the very
set chosen by the COM designers is the ideal one for the use
cases _they_ were considering -- local, in-process, and fast-
response-LAN client/server usage -- but, change the use cases,
and the tradeoffs shift... and so does the optimal definition
of object-identity rules, cfr. for example the .NET choices).

> To me an interface means
> a contract by wich both a component and the client code live. All I am

That's quite a different issue -- the COM identity-semantics
constraints have nothing much to do with programming-as-contracting
(you can't even specify pre- and post-conditions in COM's own
terms), nor does p-as-c imply (e.g.) that two calls to QI
with the same parameters must both fail or both succeed (and
return the same result in the very specific case where the
interface ID being requested is IID_IUnknown, but without the
same constraint in other cases).

> saying is that the proposed definition is to vague, and I don't
particularly
> care for it. To me the definition of an interface that only guarantees the
> fact that a class will have attributes with particular names of type
"bound
> function" is handicapped and useles!

The extra guarantee that the methods' signatures also match
is not significant added value, though -- and THAT is all
that any language in the world ever buys you at compile
time (if even that -- add any kind of co-variance, which both
Eiffel and Java offer in different forms [Java has it for
arrays], and you're back to runtime checking _even just
for "type-safety"_...!!!).

That may be extremely irking to you on a philosophical plane,
but there is really no way to check up "at compile time" on
any of the kind of semantical constraints we're truly most
interested in -- they're not mere syntacticalities (method
names or signature), but always deeper ones, requiring the
use of existential and universal quantifiers in all cases of
real interest... which makes them impractical to check at\
ANY time in production code/runs.

E.g., we have a method which takes two collections, and the
constraint may be that every integer in the former exactly
divides (with no remained) every integer in the latter.  No
compiler known to man or beast will ever check THAT for you,
and, even at runtime, the O(M*N) cost of the check makes it
impractical to perform (except in runs specifically devoted
to testing purposes, i.e., not "production" runs).

Or, take the semantic constraints on a predicate that can
be used for ordering in the C++ Standard Library ('weak
partial ordering'): P is an acceptable parameter iff...:
    -- for any x, not P(x,x)
    -- for any x, y, P(x,y) implies not P(y,x)
    -- for any x, y, z, P(x,y) and P(y,z) imply P(x,z)
I doubt anybody even DREAMS of a compiler able to check
such constraints (or, of really checking them at runtime,
exhaustively, even in the most aggressive unit-testing
strategy -- at most, they'll be checked on a few sample
values of x, y, z).

Does this make _every_ language in the world "handicapped
and useles"?  A philosopher could take such a stance, sure.
A _pragmatical_ person, on the other hand, takes into due
account these limitations _and still documents these kinds
of constraints_ as formally as feasible given whatever
technology is at hand.  (The same goes for _performance_
semantics constraints, such as the C++ Standard Library
widely introduces: no way to enforce them, or have any sort
of _guarantee_, but that doesn't mean they don't still play
a *crucial* role in determining library usage and design!).


> >I see nothing in the interface-adaptation PEP that can be said to
> >be borrowed from other languages (even if one counted COM as "a
> >language", which of course it isn't -- it's an object-model
> To be precise "Component Object Model".
> >, which
> >can be, and is, implemented by a huge variety of languages), and
> >most particularly nothing that somehow depends on 'staticity' vs
> >Python's 'dynamism'.
> Oh, Lord! Leave the COM alone. Why don't you talk about similarities with
> Java?

Because YOU mentioned COM -- a very interesting and hugely successful
design for a cross-language object-model, which succeeds in good part
by doing the very OPPOSITE of what you SAID it was doing [the idea of
taking COM as an EXAMPLE of "compile-time" was particularly hilarious
to a COM expert, and I wanted to share the mirth:-)... and possibly
help dispell some misconceptions].

I'm not particularly interested in Java's object-model in this context,
nor does my Java expertise reach anywhere near the extent of that on
COM (I _have_ studied the sources of typical JVM's, of course, but I
don't think I could implement one without substantial refreshing of
such studies, for example).  The misconceptions regarding Java appear
to be less widespread and less pernicious (few know about the specific
hole that arrays introduce in its purported compile-time type-safety,
but that's a rather specific issue; in practical Java use, a Vector
is used instead of an array, and *THAT* is *OBVIOUSLY* not compile-time
type-safe since ANY object can be stuffed into it -- it doesn't take
much Java knowlegde to point this out:-).

> And in my mind, the notion of the interface is far more valuable for
> staticaly types languages, component models, what have you. And could
please

The notions of interface and contract are extremely useful in all
kinds of software _design_ -- they are primarily design-tools, and
only secondarily coding-ones.  And whoever thinks that a good design
is less valuable when a dynamically-typed language, rather than a
statically-typed one, is to be used for the coding, needs far more
reprogramming than I can supply:-).

> somebody explain to me what advantages the proposed PEP give us over
simple
> documentation.

Formalizing the concept of interfaces avoids the horrid ambiguities
that current documentation mentioning informal 'interfaces' brims
with.  Any time some docs mention 'a file-like object' you never
know WHAT the component needs form the object -- just .write, or
also .writelines, or perhaps .read, or .readlines, or...??!?!  And
whenever a 'sequence' is mentioned, you never know whether you
need to supply a __length__ (or just a suitable __getitem__) and
whether that __length__, if it does have to be present, can be
purely indicative or must be exactly correct.  Just to mention two
out of many, many such issues.


> BTW. Alex, you mentioned that Python supports COM well... As you can see
> interface support could be implemented using existing language constructs.

If you include C-coded extensions among the "existing language
constructs" of Python, then, yes, I cannot think of ANYTHING
that cannot be _implemented_ using them.  There may be no clear
way of _expressing_ what is going on in Python, when enough of
the crucial stuff is hidden in a C-coded extension (have you
*studied* the current [and excellent] implementation of the COM
support, on both the C-coded and Python-coded sides...?!-).


Alex






More information about the Python-list mailing list