[Python-3000] PEP 3100 Comments
Talin
talin at acm.org
Tue May 9 08:19:59 CEST 2006
Jim Jewett <jimjjewett <at> gmail.com> writes:
> On 5/8/06, Talin <talin <at> acm.org> wrote:
> > Before we go to much further on this point, I want to get a sense of
> > what exactly people are objecting to:
>
> > -- Are they saying that interface discovery is not important, or is
> > bad style?
>
> Not in principle.
>
> In practice, certain implementation patterns can be bad style.
>
> In general, python encourages "just try it" instead of "verify the
> preconditions, then try it if you can".
>
> Obviously, there are times when another style makes more sense, but
> the effort to decide that exceeds the effort of implementing it; by
> default the language should encourage the way that is normally better.
>
> > -- Are they saying that the way isSequence and such implements
> > interface discovery is wrong, and if it is, what's the alternative?
>
> Yes, it is wrong. There isn't a good (and general) alternative in 2.x.
>
> The working assumption is that there won't really be a good general
> way in 3 either, unless what you really mean is "Does it have these
> methods", in which case the best way is to test for those methods.
> isSequence may give false confidence in the answers.
You know, I am sure that a lot of people are tired of this thread, but
I really think that there's a possibility of something constructive
coming out of this.
Rather than arguing about the current library, I would like to
identify what are the fundamental requirements that people need (and
I'm speaking in terms of observed code patterns from existing Python
libs), and how best to go about meeting those needs.
Python is, as has been said, deeply invested in duck typing; This is
one the things that makes the language neat and special. In
particular, duck typing increases modularity by allowing different
pieces of code, developed independently, to interoperate. Any
technique that allows for interoperability of independently created
parts is going to be a big win in terms of distributed development.
For example, in a traditional OOP system, I can create an interface
that accepts objects of a given type or any specialization of that
type. Anyone who wishes to use my implementation must derive from my
abstract types -- in other words, they need to import my code or link
against it somehow.
With duck typing, you don't need to link against my code, all you need
to do is follow the same specification. It means that I, as an
interface designer, can specify a kind of 'abstract model' of a type,
and other people can create their own types that conform to the
restrictions of my model, even if they don't actually have a copy of
my code. All they need to do is read my docs.
(Its interesting to note that C++ templates also give you duck typing
in just this way. For example let's say I have a template class that
implements a refcounting "smart pointer" -- which is a fairly common
C++ idiom these days. The object being refcounted does *not* need to
derive from a common "refcountable" base class -- it simply has to
have IncRef/DecRef methods that have compatible signatures. And
indeed, several people have proposed methods of "testing" C++ classes
to see if they support specific "Concepts", in other words, specific
capabilities.)
OK, so I hope I have established the requirement for duck typing. Now,
I want to establish the requirement for interface discovery or 'type
testing'. Type testing usually comes into play when you are dealing
with interactions between two different class hierarchies. For
example, you might have M different classes that need to be serialized
with N different serializers. Or M different types of events which are
viewed in N different views. (Think of a music scoring program, which
displays various kinds of Midi events in a piano roll view, a drum
machine view, a text view, or a music notation view.) You can use
polymorphism to dispatch to different implementations based on type,
but you can only do it for one type at a time. In the music program
example, either the various editor views have to test the types of the
individual events, or the individual events have to test the types of
the editors.
Now, generic functions are good at dealing with these kinds of
situations. However, generic functions (as they are usually concieved)
can only deal with specific, concrete types, not "types which satisfy
some constraint".
Thus we have a tension between duck types and type testing;
Unfortunately, punting on this issue (i.e. saying "you can't do type
testing on duck types") isn't a good option - both techniques are just
too darn useful to say that you can't use them together.
Suppose I want to be able to create an interface that takes either a
built-in sequence - either a list or tuple - or a user-created
sequence type. Moreoever, I want people to be able to create new types
that also satisfy the requirements of my interface.
I'd like to be able to support user-created objects that are not
derived from the built-in sequence types - because it may be that the
internal representation of the sequence is very different from the
built-ins. There's no reason why my interface should be restricted to
using "official" sequence types, as long as the custom types behave
enough like a sequence.
So the question is - how does the person writing their own sequence
type indicate that their type is to be treated as equivalent to a
sequence, at least for purposes of my API?
A second, related question is - how much like a sequence does the
object have to be? For example, there are many APIs that really only
care if the object is iterable or not.
Most APIs use only a fraction of the full capabilities of a type - so
perhaps what is needed is a way to test only those capabilities. For
example, instead of "isSequence" we could have "isIndexable",
"isIterable", and so on.
Another possibility is to have a special, empty base class that
signals "this object is a sequence". However, you'd also need to
implement the sequence methods, otherwise your advertisement of
"sequence-ness" would be false. At which point does the base class
then become redundant? I'm not sure.
Of course, we could instead somehow mandate that by deriving from that
base class, you are required to implement those methods. However,
that's just a fancy way of saying "interfaces a la Java". Interfaces
are good, but they don't allow the kind of isolation that true duck
typing allows.
Anyway, that's food for thought.
-- Talin
More information about the Python-3000
mailing list