some comments for Python 3000

Alex Martelli alex at magenta.com
Tue Aug 15 17:33:14 EDT 2000


"Greg" <gmc333NOgmSPAM at my-deja.com.invalid> wrote in message
news:1a38c7ce.825fba1b at usw-ex0103-024.remarq.com...
    [snip: C++ vs Eiffel]
> > > and obviously so much better
> >
> > Nothing obvious about it.  Syntax more verbose, OK, what do I
> care;
>
> Two words: "include files". At least in Eiffel (and many other
> languages) you don't have to declare everything twice.

In Python I don't have to declare it even once.  In component
based development, I prefer to write the declarations anyway,
in IDL, after which any decent tool will generate the stubs in
whatever language[s] I want to use to implement or consume
the interfaces in question.  I like the separation of interface
and implementation, which I don't see as "declaring twice".

There's nothing _obvious_, in my opinion, about it being
"better" to blur the interface/implementation distinction. It's
a design choice; IMHO, where it lets me do away with any
need for declarations, as in Python, it may be worth it, but,
otherwise, it isn't.

But I need not convince anybody about either thesis -- I'm
only saying it's absurd to call either choice *OBVIOUSLY*
superior to the other.  To reiterate: there is absolutely
nothing OBVIOUS about it!


> Perhaps more important than syntactic differences are semantic
> or conceptual differences. For example, Eiffel's genericity is
> much more expressive than C++ templates. Eiffel allows for
> constrained genericity, so if you declare a sorted container,
> you can specify that the objects in the container support the
> notion of being comparable. When the compiler detects that
> you're violating this, you get an obvious error message. Last
> time I tried something like that with C++ templates, the
> compiler either remained silent or issued very obscure error
> messages.

C++'s approach to templates' type-safety is extremely close to
that of Python!  The types you're allowed to use to instantiate
a C++ template, just like those you can pass to a Python
function, are, basically, any that will supply all the actually
needed functionality: all of those, and those only.  No extra
baggage (or, as little as is compatible with doing the checks
at compile-time: if a function is only needed on an if/else
branch that is actually not taken, it will still be requested).

If you WANT the type T to be one that inherits from Comparable,
you assert this explicitly, e.g.:
    template <typename T>
    bool fooz(const T& t, const T& u)
    {
        Comparable* pt = &t;
        return (*pt) < u;
    }
This is normally not needed, just as checks on type(t) would
normally not be used in Python -- you just "return t<u" and
could care less about how T implements that.  A standard
compliant C++ compiler *has* to give an error message
where fooz<T> is instantiated if T is such as not to let you
write that comparison; if you met one that didn't, it was
just a buggy piece of software.  The quality of error messages,
whether they're clear or murky, are obviously an issue of
specific implementations.

What VC++, not normally thought of as a very high-quality
implementation of C++, tells me when I try to instantiate
a template using operator<, on a type that does not support
it, is the following error message:
"""
'const struct Pep' does not define this operator or a
conversion to a type acceptable to the predefined operator
"""

Is this "very obscure"?  If all the error messages you ever
get from your compilers are clearer and more limpid than
these, I truly envy you, you know.  Personally, I find it
easily lucid enough for my own use.

More to the point, is it OBVIOUSLY better to have added
language mechanisms for "constrained genericity", where
genericity without such constraints can be considered by
many people a preferable idiom (just like many of us
prefer Python's runtime genericity -- but in this case, since
error messages come at compile-time anyway, there's
even less to say for constrained genericity than there may
be for other sorts of type-checking), and when you want
constraints it's rather easy to express them anyway?!

You must have a rather different idea of what "obvious"
means, than any dictionary I know cares to record.


> There are other semantic differences as well, such as design by
> contract and for-real garbage collection.

Yes, Eiffel has those as built-in; in C++, you have to get
them from external libraries, not intrinsic to the language
(Boehm's GC library, for example, or Great Circle's
commercial product; any of the several packages for
programming-by-contract that you can get from the net
and use with C++).

I'd much rather have a smaller language (not that anybody
would accuse C++ of being SMALL, but it's surely SMALLER
than it would be if it also contained in the language itself
stuff that can easily be added with libraries...!) and more
external libraries, than vice versa.

By not forcing on the world its own vision of, e.g., garbage
collection, C++ is better placed to "play well with others",
its key strength -- the one that helped it soar, for all the
fatuous claims of "OBVIOUS superiority" always raised by
paladins of other languages.  It doesn't have its own GC, so
it can play well with frameworks such as COM or .NET, that
specify semantically different and incompatible semantics
for GC; it doesn't force any notions about how contracts
are specified or what happens when they're violated, so,
again, it plays well with frameworks specifying whatever
semantics for those; it doesn't force a model of threading
and synchronization, of GUI access, of database interface,
of web-access, and so on, and so forth.

Is it OBVIOUSLY better to stuff any or all of these key
issues right into the language, rather than leaving each
and every one of them to external libraries?  I definitely
DON'T think so!  It's a series of trade-offs, of design
choices.  I admire and respect the specific design choices
that led to the current standard for C++ -- I would not
have taken each one exactly in the same way, had I been
the one faced with those ambitious design objectives, but
I most definitely don't have the hubris to claim that there
are OBVIOUSLY better sets of choices, along this huge
number of design-decision forks, than those taken by the
C++ designer[s].

But don't kid yourself that one can't do garbage collection
or DBC in C++ -- we practice both regularly.  Just like
accessign the Web, or databases, in Eiffel -- although THAT
was not designed right into the language, no doubt they
manage to do it anyway, don't they?  Sure, maybe it would
be a tad more convenient if it was hard-wired right into
the language -- and maybe not, because such hard-wiring
forces a programmer's hand and may often make it harder
to "play well with others".

So, once again -- what's OBVIOUS about it...?!


> > Basically C++ is about "all power to the programmer" -- fully
> > including the power to do very, very bad things.  Eiffel is about
> > more rigidity in an attempt to constrain programmers away from
> > doing very bad things.  Nothing obviously "better" about either
> > approach -- when you're targeting a segment of programmers
> > with extremely high professionality, at least.
>
> One man's straight jacket is another man's safety net, I
> suppose. What you see as rigidity I see as an attempt by the
> language to prevent errors from ever being executed. The more

"attempt to constrain ... from doing very bad things" is exactly
how I phrased it, as you just quoted.  The extra rigidity -- as,
again, I phrased it right from the start: reread what you just
quoted! -- is the means to this attempted end -- the price one
pays.  Is it OBVIOUSLY worth paying?  If you think so, why
are you interested in Python, which goes MUCH, MUCH FURTHER
than C++ in reducing compile-time "safety nets" (and gains
a lot of simplicity, flexibility, and power thereby)...?

I claim there's nothing OBVIOUS in the choice of constraining
the programmer in the cause of reducing runtime errors -- or
actually in the AMOUNT of constraints one places.  Eiffel allows
co-variance, which can easily cause runtime errors, too (or
used to, back when I had to commit to either Eiffel or C++; I
know they've expended huge efforts on that, but I suspect it's
a Turing-complete problem to try to eliminate such errors,
except by eliminating co-variance altogether [which Sather and
C++ do, and I've never felt constrained at all by THAT:-)]).

And it lets you do divisions (which could be by zero), additions
(which could overflow), etc, etc.  So it can't even APPROACH
"preventing errors from ever being executed"; it can maybe
REDUCE the amount of errors that show up at run-time, just
as C++'s own checks (e.g., no co-variance!) reduce them wrt
those Eiffel allows.

Once again: where's the "OBVIOUS" part, please?  State all
the theses you like, whether they be reasonable or far-fetched;
but I think one exceeds the boundary of common courtesy by,
not only asserting a reasonable but very controversial thesis,
but explicitly stating it's *OBVIOUS* that said thesis holds --
when it's OBVIOUSLY anything BUT "obvious".


> the compiler can detect up front, the less time spent in tedious
> late-night debug sessions. On the other hand some programmers
> enjoy taking out their own garbage rather than letting the
> compiler do it for them.

And some need to produce code that will cooperate with a
framework that has its own ideas about how garbage is
collected.  If they chose to use a language with its own
incompatible ideas about such garbage collection, then its
"OBVIOUS" superiority is going to count not for one penny
when the language and the framework just won't play
together -- maybe somebody (NOT working in the "obviously
superior" language) will manage some kludge for impedance
matching between the two, maybe they won't, and meanwhile
your competitors with "obviously inferior" languages are
beating you to market, seizing the window of opportunity,
and eating your lunch.

C++ _plays well with others_.  So does Python.  Such ability
to infiltrate diverse niches promises well for the survival and
flourishing of Python -- just as it did wonders for C++.  Sure,
Python manages to do it with incredible _elegance_, which
can't be said for C++; the latter can however claim it focused
on performance, at a time where all-out performance was
more often crucial (or perceived as crucial) than it is today,
or will be in the future.  As excellent performance may still be
needed for some hot-spots, say 10% or 20% of components
overall (depending on application area), a Python/C++ mix
is how I foresee my own future development projects.  And
thanks to "CXX", they play just as well with each other as
they play with others yet.  Can your favourite "obviously
superior" language (be it ML, Ada, Oberon, Eiffel, ...?) claim
similar weed-like hardiness, ability to infitrate ecological
niches, multi-paradigm versatility...?  Or does it have to
make do with the "OBVIOUS" nature of its inborn superiority
and forego the mundane task of actually *proving* it?-)


Alex






More information about the Python-list mailing list