Could Python supplant Java?

James J. Besemer jb at cascade-sys.com
Fri Aug 23 19:14:08 CEST 2002


Paul Foley wrote:

> On Fri, 23 Aug 2002 04:12:48 -0700, James J Besemer wrote:
>
> >      class A {
> >          int x;
> >          int bar(){...}
> >      };
>
> >      class B : A {
> >          int y;
> >          int bar(){...}
> >      };
>
> >      void foo( A a ){
> >          a.x;
> >          a.bar();    // calls A.bar()
> >      }
>
> Yes, I know.  But if you pass it a B instance, it ought to call
> B.bar(), not A.bar().  What C++ does is Just Plain Wrong.

Just Plain Wrong, certainly depends on what you're used to.

C++ always tries to cast incoming arguments to the specified type if a cast is
available.  This rule predates C++, having originated in C.  It eliminated a lot of
nuisance with all the various number types.  Conversions automatically exist for
sub classes to super classes so (unless you make other arrangements) they are cast
to the requested argument.  In this case it is not what You (fairly) happen to
expect.

Casts are NOT automatically generated for pointers, so the following code WILL
exhibit the expected Polymorphism without virtual functions (classes A and B as
above):

    void foo( A* arg ){
        arg.x;
        arg.bar();        // calls A or B depending on type of arg
    }

    A a; B b;

    foo( &a );    // foo will call A.bar()
    foo( &b );    // foo will call B.bar()

Right or wrong, C++ usually uses pointers to handle most non-trivial objects, so
the conflict between argument conversions and polymorphism is no big hardship.

I don't defend C++s semantics; I'm just trying to explain.  I think most people
agree that casting args where possible overall is a Good Thing.  If you knew the
language better you might not think it's so "Wrong."  But I won't argue that point.

Anyway, C++ clearly does support Polymorphism without any "late binding".

> You only get Polymorphism if you declare your member functions to be
> "virtual", in which case it does late binding -- which is no help to
> your argument that you don't need late binding for polymorphism.

Not true on two counts.  See below.

> > be distinguished if they have different argument lists.  a.bar() is
> > unambiguous but b.bar() is not.  b.bar() is resolved in favor of the most
> > recent declaration.  There's a scope resolution operator to override
> > defaults.
>
> In other words, there's no polymorphism; you have to dispatch to the
> right place manually...assuming you can even know what the right place
> will be.

No.  "Scope resolution" in this context is the same as in Python if, say, you need
to access a super class's instance that has been overridden by a subclass.  In C++
you use the scope resolution operator when default scope resolution is not what you
want.  In Python, you would use the super() operator if the diamond rule doesn't do
what you want.  Actually somewhat unrelated to Polymorphism, except that name
conflicts generally don't arise without classes and sub classes.

> > This is all completely static.
>
> Yes.  Also completely lacking in polymorphism, so what was the point?

Clearly we disagree on the definition of Polymorphism.  I think most authorities
agree that C++ has Polymorphism as demonstrated above.

    http://www.cyberdyne-object-sys.com/oofaq/oo-faq-S-2.1.0.5.html

    http://www.webopedia.com/TERM/O/overloading.html

The latter reference points out that C++ provides genuine Polymorphism without
using objects at all:

    void IsaT1( T1 a ){ return 1; }
    void IsaT1( T2 b ){ return 0; }

    IsaT1( x );    // returns 0 or 1 depending on arg type

This particularly useful form of Polymorphism is sorely lacking in Python.  E..g,
picture a vector class and you want to define operators + and * for vector addition
and for vector multiply.  So far so good.  But then you want also to define + and *
for vector-scalar operations.  In Python you're hosed as there is no way to
discriminate class functions by argument signature.  I hope that eventually
changes.

Virtual functions provide another, more general kind of Polymorphism.

> And just what do you think your C++ vtable lookup is?

> You've failed to demonstrate that.  You had to resort to late binding,
> via `virtual', to get polymorphism in C++

A vtable is a constant array of pointers to virtual functions.  All instances of
classes in a class hierarchy have a constant pointer to the table.  Since the
expensive task mapping names to addresses is resolved at link time, I and most
people fairly consider it "early binding".  FWIW, I use early or late binding to
characterize languages as a whole, not individual language features.

"Early binding" generally means resolved at or before link time.  The target
function's address is decided at link time and which function gets called is
entirely determined in advance, depending on the instance object's ID.  Symbol
table information generally is no longer available to the runtime environment, so
no eval() or dir() functions are possible.

"Late binding" generally means resolved at runtime.  E.g., the same function name
on the same object may refer to completely different definitions from one call to
the next, a feature lacking in Early Binding languages.  All symbol table info
typically is available, thus you can eval() and dir() to your hearts content.

Yes, to implement virtual functions there is a vtable "lookup" at runtime but it's
not a dictionary lookup ala Python.  In most implementations, the vtable is a true
array and the lookup is simply an additional level of indirection.  Thus virtual
functions are almost as fast as regular functions, way, way faster than a Python
function call.

Borland's Delphi is another C++-like language that implements Polymorphism with
Early Binding.  It avoids some of C++ idiosyncracies (such as casting calling args)
and may serve as a better example of Polymorphism in Early Binding language.  It's
object model is different and never uses explicit pointers to objects.  Thus the
Delphi version of the first example above behaves exactly as You expect when
passing in a or b, without resorting to pointer indirection.  Of course, being
Pascal based, Delphi comes with it's own idiosyncracies.

If you don't agree on my definition of early vs. late and my classificaiton of
vtable lookup as an early binding scenario -- and more importantly my claim that
you true Polymorphism is available in "early binding" languages -- then I'm sorry I
wasted my time.

--jb

--
James J. Besemer  503-280-0838 voice
http://cascade-sys.com  503-280-0375 fax
mailto:jb at cascade-sys.com






More information about the Python-list mailing list