The Artima Developer Community
Sponsored Link

Weblogs Forum
Interfaces or Abstract Base Classes?

42 replies on 3 pages. Most recent reply: Dec 16, 2005 1:16 PM by Ed Suominen

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 42 replies on 3 pages [ « | 1 2 3 ]
Guido van van Rossum

Posts: 359
Nickname: guido
Registered: Apr, 2003

Re: Interfaces or Abstract Base Classes? Posted: Aug 14, 2005 2:07 PM
Reply to this message Reply
Advertisement
[Phillip Eby]
> The idea of monkey typing is that you can define the
> notion of "file-like" by saying that some method of one
> type is "like" some method of another.

It would have been simpler if you didn't support renaming -- that just makes it more confusing.

> Or, at a higher
> level, you can simply say that one class is "like"
> another, in the sense that any of its methods with the
> same names are "like" the methods in the other class.

So that's an interface, right?

And I suppose the argument types should also be declared -- recursively using monkey typing, or perhaps using interfaces (specifying in which ways a read() method uses the "nbytes" argument seems more work than just saying it must be an int).

> Monkey typing also fixes one of the obvious problems with
> the proposed "file" interface: optional methods or
> attributes, leading to partial implementations of
> interfaces. In monkey typing, the unit of declaration is
> a method or attribute, not an entire interface.

But in practice won't you frequently need to talk about a set of methods that go together? If you introduce a name for that, it would be an interface, again.

> For my own personal purposes, however, I find that
> extensible generic functions are a better solution than
> interfaces for most practical problems. Python in fact
> uses extensible generic functions for many purposes
> already: copy() and pickle(), for example. If such
> generic functions were first-class entities in Python, we
> could use concepts like 'file.read(ob, bytecount)' to do
> "file reading" on someob, rather than guessing at the
> semantics of ob.read. But it requires rethinking one's
> approach to OO.

Why is guessing the semantics of ob.read less reliable than guessing the semantics of ob.__read__?

Phillip J. Eby

Posts: 28
Nickname: pje
Registered: Dec, 2004

Re: Interfaces or Abstract Base Classes? Posted: Aug 14, 2005 6:12 PM
Reply to this message Reply
> [Phillip Eby]
> > The idea of monkey typing is that you can define the
> > notion of "file-like" by saying that some method of one
> > type is "like" some method of another.
>
> It would have been simpler if you didn't support renaming
> -- that just makes it more confusing.

True; however the idea was to support PEP 246-style generality. Generic functions don't need this, of course, because that would be like renaming the 'copy()' function: irrelevant to the semantics.


> > Or, at a higher
> > level, you can simply say that one class is "like"
> > another, in the sense that any of its methods with the
> > same names are "like" the methods in the other class.
>
> So that's an interface, right?

Sure - but an interface that can be extracted retroactively from existing code, without the original developer needing to take the time to factor it out. Every other interface proposal thus far (that I know of, anyway) has doomed us to try to reverse engineer "IFile" (or whatever you want to call it). And if you try to write 'IFile' before you've written 'file', you may end up with a premature or wasted abstraction anyway.

> And I suppose the argument types should also be declared
> -- recursively using monkey typing, or perhaps using
> interfaces

No, just say what type the argument must be "like".


> (specifying in which ways a read() method uses
> the "nbytes" argument seems more work than just saying it
> must be an int).

Just say it's an int. The operations needed can be inferred from the code, if some kind of type checking tool wants to warn you that the argument you supplied isn't int-like enough.


> But in practice won't you frequently need to talk about a
> set of methods that go together? If you introduce a name
> for that, it would be an interface, again.

In the common case, it's just a type, like 'file' or 'dict'. Or, if you want to be explicitly abstract, you could use an abstract class.


> > For my own personal purposes, however, I find that
> > extensible generic functions are a better solution than
> > interfaces for most practical problems. Python in fact
> > uses extensible generic functions for many purposes
> > already: copy() and pickle(), for example. If such
> > generic functions were first-class entities in Python,
> we
> > could use concepts like 'file.read(ob, bytecount)' to
> do
> > "file reading" on someob, rather than guessing at the
> > semantics of ob.read. But it requires rethinking one's
> > approach to OO.
>
> Why is guessing the semantics of ob.read less reliable
> than guessing the semantics of ob.__read__?

The semantics of special methods are defined by the language, and most people don't go around using special names for "normal" methods, which is what prevents collisions.

To look at it another way, consider this. Most names in a Python program are in a well-known namespace. By that I mean, if I import foo.bar, I know that 'bar' is in the namespace of 'foo'. Barring extreme dynamicism, this gives me a relatively-stable guarantee as to what 'foo.bar.baz' will get me - what it "means" in some sense.

However, for function arguments and return values, I have an object whose namespace is unknown, and my expectations of it are implicit. I don't know what I will get when I dereference a name! This strikes precisely to the heart of type declarations. I want to be able to say what I will find in the namespace of a particular object. __special__ methods get a pass by being part of a *reserved* namespace, so they don't really count here.

The generic function solution to this problem is to call known functions on objects, so the intended semantics of the operation are statically known, even if the actual implementation invoked is dynamically registered. The monkey typing solution, on the other hand, just replaces the object with a namespace that maps the desired names to the available implementations. Both work, however, by having some *object* (that can be obtained from a well-known namespace) as a reference point: either the generic function (e.g. copy()) itself, or the type's operations in the monkey typing case.

(Oh, by the way, I think I remember now where the ABC thing came from. It was a discussion that you, Jim Fulton, Tim Peters and I were having in private mail a couple of years ago, just before I went off to write PyProtocols. Tim made an rather compelling case for the values of ABCs over interfaces, and I believe you indicated that you thought his arguments had some merit, although I haven't yet tracked down the whole thread yet.)

Steffen

Posts: 4
Nickname: steffen
Registered: Aug, 2005

Re: Interfaces or Abstract Base Classes? Posted: Aug 17, 2005 6:33 PM
Reply to this message Reply
Python's evolution
seems sure to depend upon the interface
semantic desired. What Python becomes is at
stake. I dread the direction this discussion
is heading. Let me try to explain.

IMHO, there are different kinds of abstraction.
There can be many kinds of organizational schemes
for the same (set of app. domain) 'objects'. For
instance, life-forms can be organized in ways
that recursively reflect varying levels of commonality. A biological taxonomy is one way to organize life forms. Another way to organize these same life forms might
involve human nutritional opportunities. Presuming
that all life forms have certain properties/methods
(of interest from the nutritional perspective), then
various life form instances might support something
like a "fat-source" interface - from some "food-source"
interface hiearchy.

In terms of a biological taxonomic 'class' hierarchy,
an avocado has virtually nothing in commmon with
a pig. From the "fat-source" interface hierarchy
perspective, both the Avocado and the Pig are
"fat-sources". It depends on the perspective
needed for the job at hand.

If interfaces just materialize alternate "orgainzational
schemes", for some given set of (app domain) objects,
then there is no necessary coupling to the type
system. If you are going to require a property named
"average_fat_grams_at_maturity", then the fact that
this property is 'typeless' is irrelevant. The inteface
just means that such a property name must exist -
*and* that some (implied) semantic goes along with
such a named property. The type system of the
underlying programming language tends to naturally
appear in the interface construct appropriate for
that language. I see no necessary/desirable coupling
between interfaces and strong typing.

I'd also argue that the 'implementation' logic/code
itself *should* be organized along only *one* of the
possible organizational schemes. Traditionally, this
is the one manifest by the 'class' hierarchy. Why
scatter object implementation logic into even more
places? If you do, you end up with something
very much like aspect oriented programming. You
get code weaving at run-time. It seems like this
would muddle 'aspects' and 'abstraction'.
Muddling such distinct programming language
concepts/principles seems extremely dangerous.

On the other hand, adaptation/mapping logic/code
can be added to make an interface work with otherwise
incompatible objects. For instance, a stub in
an interface might supply a constant value in place
of an otherwise missing property. This might be
done only when property binding fails (at run-time).

As I see it, adaptation logic should only
augment/wrap an (app. domain) object (or perhaps
a graph of objects which are rooted at some given
'root' object). Adaptation logic does NOT change
what individual objects represent/mean/do.
There probably should be some limits
on adaptation logic (e.g. no persistent property
additions, etc.).

I don't design programming languages very often.
Maybe I just don't get it. But if I'm recognizing
what is going on here, I'm worried about Python's
future. I think I see distinct programming
language concepts being muddled together - and
then forced into a quite unnecessary coupling to
strong typing.

Am I missing something fundamental?

Gregg Wonderly

Posts: 317
Nickname: greggwon
Registered: Apr, 2003

Re: Interfaces or Abstract Base Classes? Posted: Aug 17, 2005 7:28 PM
Reply to this message Reply
> I don't design programming languages very often.
> Maybe I just don't get it. But if I'm recognizing
> what is going on here, I'm worried about Python's
> future. I think I see distinct programming
> language concepts being muddled together - and
> then forced into a quite unnecessary coupling to
> strong typing.
>
> Am I missing something fundamental?

I think there are two distinct issues at hand. Interfaces and typing. Interfaces provide a typing mechanism at the class level. They say this class has this set of attributes.

The secondary issue is whether the type of the interface arguments should also be typed. And in order to do that, all variables in python could be given the ability to demand a type. At runtime, a typed value, when assigned to a typed variable can be checked for type correctness. If a static compilation or validation environement every exists for python, then there could be some earlier notification of contracts not being met for interworking via the interface and/or typed arguments/variables/attributes.

There is a good visual description of a non-typed interface implementation at http://python.miscellaneousmirror.org/peps/pep-0245.html

The question for typing is what value does it add or subtract from the language. Typing a class with an interface is a very powerful way of specifying the contract of interaction that the class supports. It says when you find one of these methods, you'll find all of them.

Adding typing of variables is another layer. Syntax is one component, but semantics are even more important. If you do type checking at runtime, as already happens for builtin language operations, then adding type checking on variable assignment is just an extension of this concept to one more operation, assignment.

It might be that there are some more subtle issues based on implementation details. But, there are some very powerful things that come out of allowing typing to be expressed in languages where it really does matter. If a language supports polymorphism, then typed arguments can make it much more obvious to the reader, which functionality is being called, instead of having to sift through a bunch of conditional logic that is doing type comparison.

In Java, before generics, we all used Object as the 'untyped' argument to methods that were very general purpose and which specialization through polymorphism was not well defined.

You can imagine the conversion of an Object to a numeric value. Something like the following is an untyped
conversion from some type to a number.

public long numeric(Object n) {
try {
Long.parseLong( n.toString() );
} catch( NumberFormatException ex ) {
throw new IllegalArgumentException(
"unexpected type: "
n.getClass().getName()+" for value="+n );
}
}

This is a simple, contrived example. Without typing, this is what you'd have to code.

But, with typing, you have polymorphism, and you could then have:

public long numeric( Number n ) {
return n.longValue();
}

public long numeric( String n ) {
return Long.parseLong( n );
}

public long numeric( Object n ) {
try {
return Long.parseLong( n.toString() );
} catch( NumberFormatException ex ) {
throw new IllegalArgumentException(
"unexpected type: "+n.getClass().getName()+
" for value="+n );
}
}

and thus it becomes more clear to a reader of the code what the choice are.

Now, the runtime polymorphism is a more interesting issue. It adds overhead to the method call, and thus you would want to weigh the use of polymorphism in a dynamic language.

But, there are some posibilities for runtime optimizations by examining the types of the values that become the method argument and seeing that they are all typed, you can then say okay, I know which version to call and I'll augment the byte code to avoid the lookup on each call.

pluses and considerations all around...

Steffen

Posts: 4
Nickname: steffen
Registered: Aug, 2005

Re: Interfaces or Abstract Base Classes? Posted: Aug 18, 2005 11:40 AM
Reply to this message Reply
What is the difference between polymorphism and
(operator/method/function) overloading?

Unless I'm confused on yet another pair of 'orthogonal'
programming language concepts/principles, these
are being blended in the previous post! Ironically,
that is what Guido's original post seems to do as
well - albeit with different concepts/principles.

This recurring "blending" pattern/practice seems
to add credence to my original worries. It reminds
me how confusing things can (very quickly) become
*if* (but only if) orthogonal principles/concerns are not separated. To establish/illustrate this point, let
me get specific about the polymorphism and overloading concepts discussed in the previous post.

As I recall, one way to distinguish polymorphism
and overloading is based on the need to disambiguate
binding decisions (at whatever
compile/link/load/run binding time options are
supported by the given language's execution
environment). Let me explain.

If memory serves, polymorphism
refers to binding resolution based on the 'class'
hierarchy. In C++, polymorphism appears via
(pure/non-pure) 'virtual' methods that are
implemented (or inherited) by each derived
class. The specialized classes that implement
a polymorphic method must have *NO* ability to change
the polymorphic method signature (as declared in the
base class). Derived classes cannot change the number
of method arguments. In typed languages, the derived
classes cannot even change the type any of the
polymorphic method arguments. At any rate, the
polymorphic method syntax (i.e. signature)
*and* semantics must are fixed at the
generalized/base-class level (within the
type/class hierarchy).

Given an object (instance), the language run-time
must pick/bind-to the particular *polymorhpic*
method 'implementation' to 'invoke' (i.e. call).
This behind-the-scenes, run-time decision must
be based on the specific type of the object
being bound/processed/considered. This leads
to programming language execution environment
artifacts like C++'s "vtables". Such data
structure/processing conventions support
polymorphic binding. They make it possible to
make the run-time binding choice (on an
object/instance by object/instance basis -
for the object actually processed at run-time).

As you surely know, overloading is quite
different. When one overloads a given
method/function/operator, each instance
of an overloaded method/function/operator
will have a distinct signature. That signature
is somehow composed of the return (type/name/existence)
and/or the number-names-or-types of method arguments
(and/or some combination of such things). Just
what comprises a method/function/operator
'signature' depends on the programming language
in question. However, method/function/operator
(implementation) signatures have nothing much to
do with the structure of the type/class hierarchy!

Irrespective of the language specfic particulars, overloading leads to binding decisions that *can*
be made either at run-time *OR* at times ealier than
run-time (i.e. compile, link or load time).
Polymorphism leads to binding decisions
that *MUST* be made a run-time.

This one language implementation/support
difference, between polymorphism and overloading,
applies to any programming language. So for the
sake of brevity, let me suggest that this
difference begins to establish the fundamental
difference between overloading and polymorphism.
I'd suggest that these two programming
langauge principles are orthogonal to each other.
I'd also suggest that both polymorphism and overloading
are orthogonal to strong typing.

For instance, in some hypothetical
flavor of Python 3000, it is *possible* to imagine
support for the following 'overloaded' functions.

def numeric( a, b )

def numeric( a, b, c)

def numeric( a )

def numeric( )

Support for overloading's distinct method signatures
does NOT necessarily require strongly typed arguments!
Unless I'm mistaken, there is no coupling between
these principles/conepts. Again, I'd argue that
these principles/concepts are orthogonal.

How such orthogonal principles/concepts get blended
into any particular programming language is *just* a
programming language design choice. Each programming language has it's own 'recipe'. On the other hand,
muddling orthogonal concepts/principles, within the
syntax of single programming language, seems like
'bad language design'. Right?

On a different-but-pivotal topic, I'm fascinated
by the following assertion:

>> Interfaces provide a typing mechanism at the
class level.

As I argued previously, Python's evolution
(as a programming language) seems sure to
depend upon the 'interface' semantic pursued.

Type declarations that bind to a multiplicity
of specific types are possible. This might be
one sort of interface semantic. This choice
leads to what is traditionally called 'parametric
polymorphism'. In C++, you see parametic
polymorphism supported via the 'template' syntax
(and recently in Java via Generics - to an extent).
Is this the 'interface semantic' desired? I doubt
it ;-)

As a matter of fact, there are types of types
(or classes of classes). These concepts are pretty
rarified (still). For example, Haskell has a notion
of 'kinds' (of classes). See

http://www.haskell.org/tutorial/classes.html

Is this sort of "typing mechanism for classes"
what was being referenced?

There are other sorts of 'interface' semantics. I
was suggesting that the 'interface' semantic might
support alternate forms of abstraction (and
alternate abstraction hierarchies along these
different 'organizational axes'). I suggested that
expressing different (app. domain) 'perspectives'
justifies this sort of 'interface' semantic.
If this sort of interface semantic is desired/chosen, then opportunities to leverage such 'interfaces' for layered "adaptation" came up.

It all comes back to Guido's motivation.
It seems quite telling that Guido's intent/motivation
was quite vague. He did some arm waving at ZOPE/Twisted
as a precedent -- and then jumped into possible
'interface' syntax. This left the question of just what
this new 'interface' syntax should do very under-defined.

I'd suggest that a specific interface semantic choice
was neither made nor defined/documented. As a result,
it was all too easy to blend a variety of interface semantics into the one 'interface' syntax (proposal).
I'd suggest the first order of business ought to be
defining what 'interfaces' are supposed to support/do.
Deciding what 'interface semantic' we want to support
seems like the first question. The 'interface' support details, like the 'interface' syntax, should flow
from that. As I see it, the details are
judged by, and optimized for, the desired
'interface' semantic. I'm not sure how to discuss
an 'interface' syntax proposal without knowing the
desired 'interface' semantic ...

In spite of Guido's original blending of topics,
I'd also suggest that 'strong typing' *should* be a
completely separate topic.

Gregg Wonderly

Posts: 317
Nickname: greggwon
Registered: Apr, 2003

Re: Interfaces or Abstract Base Classes? Posted: Aug 18, 2005 12:33 PM
Reply to this message Reply
> What is the difference between polymorphism and
> (operator/method/function) overloading?

> Support for overloading's distinct method signatures
> does NOT necessarily require strongly typed arguments!
> Unless I'm mistaken, there is no coupling between
> these principles/conepts. Again, I'd argue that
> these principles/concepts are orthogonal.
def numeric( Number n )
    return n.longValue();
 
def numeric( String n )
    return Long.parseLong( n );


In python, you can do the above overloads. You have to use a single signature and then evaluate the argument to process it differently based on its value.

> On a different-but-pivotal topic, I'm fascinated
> by the following assertion:
>
> >> Interfaces provide a typing mechanism at the
> class level.
>
> As I argued previously, Python's evolution
> (as a programming language) seems sure to
> depend upon the 'interface' semantic pursued.
>
> Type declarations that bind to a multiplicity
> of specific types are possible. This might be
> one sort of interface semantic. This choice
> leads to what is traditionally called 'parametric
> polymorphism'. In C++, you see parametic
> polymorphism supported via the 'template' syntax
> (and recently in Java via Generics - to an extent).
> Is this the 'interface semantic' desired? I doubt
> it ;-)

An interface implementation that enumerates a set of method signatures that will all be available from an object implementing that interface is what I am talking about.

That 'types' the object/class. You can imagine simple concepts like 'Shape' and 'Color' in a graphics package. If 'Shape' was a class, and it implemented the 'Color' interface, then you'd have a type of 'Shape' that had 'Color'. Other types of shapes might have 'Texture'
or 'Location' etc.

Thats all I am talking about.

If, python also added optional typing, then you could have the ability, described above, to have overloaded method signatures (using ad-hoc polymorphism if you will) where the types of the arguments are different.

Without types in python, you can't have a single object implement more than one method named 'add'. With typing, you can. I guess some programmers choose to invert the call chain and have something like

def add(a)
a.add(self);

so that they can use a single implementation of add for
each object. Sometimes this is the right thing to do, other times its not and coupling occurs that creates problems.

Jayson Vantuyl

Posts: 7
Nickname: kagato
Registered: Jan, 2005

Re: Interfaces or Abstract Base Classes? Posted: Aug 18, 2005 2:03 PM
Reply to this message Reply
Why not only use interfaces in interface declarations. Mixing them together with hard types (even expressions yielding types) is IMHO a mistake. First, it makes retroactively repairing or extending interfaces difficult (usually a child interface must be made that isn't necessary for any reason other than unbreaking the parent interface).

I propose only using interfaces but being able to bind them to types. For example, you might do something like this (even though it is a silly example):


interface customer:
def bill(amount: float) -> None


This is handy for starters, but the issue arises when amount needs to be something other than an int. As a concrete example, assume the above business goes international and requires amounts of different currencies. Of course you could extend the interface, you could even create a clever currency type that derived from float and magically adjusted itself. The problem is that you always have to change a interface that is too narrow. This defeats the purpose and breaks things.

I propose this:


interface currency:
pass

interface customer:
def bill(amount: currency) -> None

@implements(customer):
class basiccustomer:
...

@implements(currency)
class euro:
...

@implements(currency)
class dollar:
...

from oldmodule import currency as legacy_currency
implements(legacy_currency,currency)


This says what we mean. We don't necessarily mean that only an int should be passed to the bill method. We mean that something that can be interpreted in that way (in this case a currency value) can be passed to that method. This is slightly more verbose, but it is infinitely flexible at runtime and encourages programmers not to underspecify simple types where later a whole host of types will be necessary.

Keeping interfaces and types distinct, with a simple declarative (made friendlier through a decorator) syntax keeps the implementation and use of interfaces extremely straightforward.

If you properly expose your registry logic, this can still accomodate interface-based adaptation (really the magic of Zope3 and Twisted) and your current plans for optimization by type inferencing. Your type inferencer would benefit from implicit duck typing (via interfaces) and run-time type-to-interface binding (due to the clever decorator syntax).

Also note how this neatly solves the need for parameterized interfaces (IMNSHO their syntax is a pox on any language). You can simply do a implements(object,parameter) to make every object in python be used but with the possibility of someday backtracking to a limited set of types.

I also fancy the though of replacing a implements decorator with actually making the interface itself callable, so it could be used as a decorator. This could be confusing, as it's not obvious what's going on. In the case of Twisted , I believe that calling an interface is used to trigger adaptation. As such, such a decision could be both confusing and disruptive.

I actually would prefer to see adaptation of the Twisted kind in a later PEP once interfaces settle, but I think that the Zope people have some different requirements that should be addressed (multi-adapters for one).

One other question though. How are *args and **kwargs handled?

One thing is for certain. Should you include interfaces in the Python language, simultaneously the standard library should grow an interfaces module (probably a built-in one) containing ISequence, IIterator, IFile, IPicklable, et al. The subsequent rewrite of the the standard library would be a boon. Instantly the standard library and all major Python frameworks can interoperate in a slightly more sane manner. Think if WSGI was spec'd as standard interfaces? What could PyEggs do with interfaces for determining ambiguous package dependencies? Will there come a day when two interface objects can be compared for equality of signature, not just name? Can this make dynamic frameworks extremely smart? I'm not sure, but the possibilities really excite me on an extremely nerdy level.

Gregg Wonderly

Posts: 317
Nickname: greggwon
Registered: Apr, 2003

Re: Interfaces or Abstract Base Classes? Posted: Aug 18, 2005 3:56 PM
Reply to this message Reply
> I propose only using interfaces but being able to bind
> them to types. For example, you might do something like
> this (even though it is a silly example):
>
>

> interface customer:
> def bill(amount: float) -> None
>


okay, straight forward.

> I propose this:
>
>

> interface currency:
> pass


This is a "Marker" interface it seems. In
this use it seems to only reintroduce the problem that interfaces try and solve. I know this is a contrived
example.

I think this type of example this is a decision that a particular design might choose if it wants to defer the implementation, but force people to look up the interfaces documentation to get more information on what should be there. In particular, the users would be forced to add a secondary interface that provided some kind of access methods.

I'd casually suggest that the model needs to include conversions instead of just types so that any software that
used a currency could get the amount in dollars, euros, lira etc, and utilize current conversion rates etc...

Steffen

Posts: 4
Nickname: steffen
Registered: Aug, 2005

Re: Interfaces or Abstract Base Classes? Posted: Aug 18, 2005 5:33 PM
Reply to this message Reply
I quite agree that 'ad hoc polymorphism' (aka
'overloading') is 'richer' (or more powerful)
when combined with a 'richer' (or more extensive)
method/operator/function signature. By combining
strong typing and interfaces, you get two additional
dimensions of functionality. This multiplies ones
options. It is more 'flexible'. It is can
be considered more 'expressive'. Yet either new
dimension of functionality can be supported either
in isolation or in combination.

I rush to agree that without sufficient
programming language 'flexibility'/'expressiveness', one
could end up forced to hand code run-time type checks on
untyped arguments. As far as I can tell, you
illustrate the necessary work arounds perfectly.

Still, it seems like a non-sequitur to conclude that
strong typing (e.g. typed method/function arguments)
are always 'better' for any programming languge.
Python, for example, has abstained from the
complexities of strong typing for a number of very
specific reasons.

There is a price to pay for Python's 'simplicity'.
The performance price is well understood (and
often discussed). The lack of strong typing also
affects the 'flexibility' of any 'ad hoc
polymorphism' (aka 'overloading'). This might be yet
another price to pay for the sake of simplicity.
Perhaps the aggregate price of Python's
simplicity (via weak typing) is starting to add up.
As new features come into the Python language, weak
typing might be more and more onerous. Perhaps the
price of weak typing simplicity is getting too
high? Perhaps not?

Should we always seek to add more and more features
to Python? Must Python become as 'expressive'
(and/or baroque) as C++? Is more always better?

Perfection is achieved, not when there is
nothing more to add, but when there is nothing
left to take away. – Antoine de Saint-Exupery

By the way, I figure that you are also quite
correct about the potential software engineering
benefits of type checking. Strong typing adds
signficant value - from earlier 'type checking'
feedback. A type checker automaton can be darn
diligent about reliably catching whole classes of
errors and omissions ...

This software engineering benefit *is* very
similar to the benefits of an 'interface' contract.
Yet Python's simplicity is at stake.

The more-is-better argument is a slippery
slope. Sometimes, languages optimized for
different purposes are better than one language
that tries to be all things for all purposes.
While we're talking about programming langauges,
this point is perhaps even clearer with respect to
domain specific langauges (DSLs) -- if anyone happens
to follow these.

Permit me to try an analogy. I own a cross-bike.
A cross-bike is a blend of a mountian bike and a
road bike. It is great for puttering around with
small children (on paved surfaces). It isn't great
for a road race. It isn't great for single track
trails (up/down mountains). The cross-bike is a
nasty compromise - but it'll get you everywhere.
All the same, the better solution is to have *both*
a mountain bike and a road bike. With *two*
different bikes, I get the best of all worlds. I
always have the choice of a bike optimized for
what I want to do. Now that I've got two such
bikes, I realize that the cross-bike is really
not very good for anything in particular. Dare
I say that cross-bikes should be considered
harmful?

If you'll permit me to spin a weak metaphor from
this lame analogy, I'm hoping that Python does not
become a cross-bike.

Personally, I quite like Python as my
quick-n-easy, RAD/agile language. I sort like the
way Python pairs up with C++ (and/or interfaces to
C++). Take performance conerns. Careful C++
code can be really fast. Good luck trying to get
a cross-bike-style language (like Java) to match
C++ performance. I know, this is a long-standing
flame war. A whole lot of folks wish that Java was
faster than C++ for every application ... but it
isn't. A lot of folks wish that RAM/disk footprints
of JAVA programs (and J2EE/JVM support environments)
were just as small as statically linked C++ programs -
but they aren't. Reality sucks. A cross-bike is
a cross-bike.

The Python/C/C++ language bundle is
probably better than *any* single language could
*ever* be. I'd hate to see Python become yet
another Java/C#/C++ equivalent. What's the point?

I quite agree with the 'interface' semantic you
seem to illustrate. Lets say that there is a
graphical object class hierarchy. Lines, shapes
and dots (pixels?) might be specializations.
Shapes might specialize into regular/geometric/vector
shapes and irregular/raster shapes. All geometric
shapes might have properties/methods/accessors
like these:
    
name (an enum like "triangle", "square", "rhombus" ...)
center_point,
locus (or locii[] ...)
edge_color_depth,
edge_red_value,
edge_green_value,
edge_blue_value,
fill_color_depth,
fill_red_value,
fill_green_value,
fill_blue_value,
edge_luminosity,
edge_width,
fill_luminosity,
edge_color_transparency,
fill_color_transparency,
edge_texture_name,
edge_texture_transparency,
fill_texture_name,
fill_texture_transparency,
texture_light_source_x_offset (from center point)
texture_light_source_y_offset (from center point)
: :
: :

Now there might also be a interface hierarchy
for 'color'. The root 'color' interface might
require only a bunch of fill_* color property/accessors.
This can be specialized into a 'Edged_Color' interface
when a bunch of 'edge_*' properties/accessors are
available.

There might also be a interface hierarchy for
'Texture'. The root 'texture' interface might
simply require some property/accessor named
'fill_texture_name'. A variety of more specialized
texture interfaces might refine the vague concept of
'Texture' in various ways (e.g. the
Shaded/3D texture 'interface', the
gradient/2D texture 'interface', etc.).

My point is simply that strong typing is *NOT*
required. I've made no use of strong types in
this elaborate Shape/Color/Texture example. If
anything, I've hinted at user-defined type extensions
like enumerations - and other such base/machine-type
(object/property/argument) 'constraints'.

I'm also trying to illustrate the 'alternate
abstraction' (i.e. perspective) 'interface'
semantic. It seems we agee on this purpose for
the 'interface' concept.

I'm suggesting that all of the 'implementation'
logic/code for the shape classes be organized into Shape
class hiearchy. Such 'interfaces' would be assert/express
commonalities/hierachies from other possible
'perspectives'.

As I mentioned, I'd avoid scattering 'implementation'
code/logic into any of the alternate 'interfaces'
hierarchies. This would lead to either some form of
'aspect oriented programming' (and code weaving) or
to multiple inheritance (from multiple, concurrent type hiearchies). Neither of these results is very
'simple'. I'm all for keeping Python as simple as
possible ;-)

At the end of the day, I'd try to solve 80% of the
common/typical needs with something 20% as
complicated. It is the last 20% of the needs - the
corner cases - that usually requires 80% of the
complexity. In other words, I'd suggest

 
* Weak typing (continue w/duck typing)
- This seems best as a separate debate/discussion
* Alternate abstraction 'interfaces' only
- Various 'perspectives' on the class hierarchy
- limited! No core/class implementation logic/code.
No property additions via interface decl.
No multiple inheritance. No implementation
weaving.
* Adaptation
- accept constraint logic/code in interfaces
> match to constraints applied to class decls
(e.g. support optional enums?)
- accept mapping/glue logic via interface decl
> when possible, such mapping/glue logic
conforms a given set of class instances to a
desired 'interface'.
> class specific 'wrapper'logic within the
'interface' decl.

Werner Schulz

Posts: 18
Nickname: werner
Registered: May, 2005

Re: Interfaces or Abstract Base Classes? Posted: Aug 19, 2005 3:47 AM
Reply to this message Reply
In the end, you really need both but you don't need two names.

An interface is a pure abstract class, i.e. it has no implementation details. An abstract class can vary some some to practically full implementation.

So you need an attribute clarifying the class intent. No attribute denotes a concrete class, "abstract" and "interface" denote abstract and interface classes, resp.

Not knowing enough about Python, I can't comment on the details, but from my Java experience I would urge not to repeat the Java syntax problems, where attributes (public, static, final, abtract) are mixed with "first-class" keywords (class, method names). Try to separate them (leaving the syntax to you as an exercise:)) as XML has done successfully.

Werner

Steffen

Posts: 4
Nickname: steffen
Registered: Aug, 2005

Re: Interfaces or Abstract Base Classes? Posted: Aug 19, 2005 7:54 PM
Reply to this message Reply
Good point Werner! I guess this takes us back to where
Guido started. He had decided to substitue some sort
of abstract base class (ABC) for the 'interface' concept.
You suggest a certain kind/name of abstract base class.
A property to clarify/assert/declare the intent of this
sort of ABC. Perhaps this is what Guido has forgotten.

We've been careening down this 'interface' path ever
since Guido couldn't remember why he had previously
decided on using ABC's instead of 'interfaces'. Guido
couldn't (or didn't) really define what he intended to
accomplish with interface. The desired interface
semnantic was left to our imagination. Instead, Guido
just suggested that ZOPE/Twisted had found it necessary
to implement some sort of 'interface'. Thus 'interfaces'
were necessary ... NOT!

I'd suggest that Guido needs to think more deeply
about what 'interfaces' are supposed to accomplish ...
and the alternatives (like ABCs). Finding some simpler
way to achieve his ends (or 80+% of his ends) would
be very 'pythonic'. Tacking on yet another
syntax/construct for yet another concept seems at
odds with Python's minimalist/clean goals.

Cheers!

Donovan Baarda

Posts: 1
Nickname: abo
Registered: Dec, 2005

Re: Interfaces or Abstract Base Classes? Posted: Dec 15, 2005 1:39 AM
Reply to this message Reply
I totally agree. Interfaces are something that appeared in languages without multiple inheritance (java, objective C, smalltalk, etc) or broken multiple inheritence (C++). All of these gave multiple inheritance a bad name.

Because people avoid it now instinctively, people have forgotten that it is a fairly intuitive way of specifying things like "is a liquid" with liquid flowy behaviour and interactions, but also "is a drink" than can be drunk.

The old OO "is a" inheritance and "has a" construction covers 95% of cases, and can be used to implement the other 5% fairly easily. Adding language extensions to do the extra 5% natively complicates the language heaps for little gain.

Ed Suominen

Posts: 1
Nickname: edsuom
Registered: Dec, 2005

Re: Interfaces or Abstract Base Classes? Posted: Dec 16, 2005 1:16 PM
Reply to this message Reply
I am an avid user of Twisted, and find its asynchronous architecture and general coding brilliance so attractive that I try to find excuses to use it even where networking isn't a priority. But I hate interfaces and haven't found a single good reason to use them yet, even when I'm getting close to the internals of the Twisted API.

I believe that the oft-cited advantage of interfaces in documenting a project is just an excuse to defer a task that most coders find undesirably. Many of the interfaces classes and empty methods defined in the Twisted project have no docstrings at all! The result is a codebase that is even more fragmented and harder to understand than if the developers had simply written abstract classes with ample docstrings in their methods, an approach that would make for seamless documentation using epydoc or some such and make it much easier to follow the source code.

Another cited advantage of interfaces, that of adapting some object for use by another, seems perfectly attainable via the well-documented capabilities of new-style classes and metaclasses. Just have your particular subclass of
Foo
use a metaclass
FooToBar
and let the
__new__
method of the metaclass take care of whatever transmogrifying that is needed of the adapted class.

Flat View: This topic has 42 replies on 3 pages [ « | 1  2  3 ]
Topic: What C++ Does Wrong Previous Topic   Next Topic Topic: Vlists, Stacks and Hash-Tables

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use