Carlos Ribeiro said:
Well, my name is not Alex, and my answer will probably fall short of a
comprehensive definition
But let's see if I can help here...
I think you did a great job! May I recommend some further reading...:
http://www.python.org/peps/pep-0246.html
http://www.aleax.it/os04_pydp.pdf
http://peak.telecommunity.com/protocol_ref/module-protocols.html
Adaptation is the act of taking one object and making it conform to a
given protocol (or interface). Adaptation is the key to make dynamic
code that takes parameters from arbitrary types work in a safe, well
behaved way.
Hear, hear!
The basic concept underlying adaptation is the "protocol", also called
"interface" in some implementations. For all purposes of this
discussion, and for simplicity reasons, we can safely assume that
protocols and interfaces are equivalent.
Right, but, for the curious...: "Interface" mostly describes a certain
set of methods and their signatures; "Protocol" adds semantics and
pragmatics -- some level of conceptualization of what the methods _do_
and constraints on how they are used (e.g. "after calling .close(), no
other method can ever be called on the object any more"). This is a
reasonably popular use of the words in question, though far from
universal.
A protocol is a set of primitives that is supported by a given object.
For example: the iterator protocol defines the following primitives:
__iter__ and next() (as documented in
http://docs.python.org/lib/typeiter.html). Any object from any class
that implement these methods, regardless of its ancestors, is said to
support the iterator protocol.
....if the semantics and pragmatics are also respected, e.g.:
x.__iter__() is x
Any object that supports the iterator protocol can be used whenever an
iterable is acceptable. This includes for loops and list
comprehensions. The biggest advantage of adaptation comes when one
realize how flexible this design is, specially when compared with
old-style type checking. In a old-style type checking environment,
parameters to a given routine must conform to the declared type of the
arguments. For iterators, it would mean that only objects descending
from some abstract class (let's say, "Iterable") would be accepted.
Now, even with multiple inheritance, this design is severely limited.
In old-style Python, inheritance isn't really the issue (except for
exceptions, where inheritance does matter). Rather, a protocol is
defined by a given set of methods.
An iterable is an object supplying a special method __iter__ which,
called without arguments, returns an iterator (any object which respects
the iterator protocol). A sequence besides being iterable supports
__len__, AND __getitem__ with integer and slice arguments, and there is
a rich semantic and pragmatic web of mutual constraints between behavior
of __getitem__ and __len__ and iteration. A mutable sequence is a
sequence which also supports __setitem__ (again with specific
constraints wrt __getitem__, __len__...) and is also supposed to expose
a rich set of other methods such as pop, append, extend, etc, etc.
This IS great BUT limited by what the LANGUAGE designer(s) sanction(s)
as 'blessed protocols'. There are quite a few, but they're never enough
to cover the needs of an application or field of applications, of
course. With protocols based on certain special methods, you have a
great concept which however is not really extensible, nor flexible
enough to help the authors of large frameworks and applications.
Framework authors do define new protocols, of course -- they can't help
doing that. "X must be an object supplying methods 'foo' and 'bar' with
the following constraints...:". This is OK for somebody who's writing
an application using just one framework -- they can code their classes
to the framework's specifications.
The problem comes in when you're writing an application that uses two or
more frameworks... the two frameworks likely haven't heard of each
other... one wants objects supplying 'foo' and 'bar, the other supplies
objects supplying 'oof' and 'rab' instead, with subtly different
semantics and pragmatics. So, what do you do then? You write adapters:
wrappers over Y with its oof and rab which provide an X with its foo and
bar. Python is _great_ at that kind of job!
But, who applies the adapters, where, when? Well, unless we do get PEP
246 in... _you_, the application writer, are the only one who can.
You'd like to spend your time developing your application, with
frameworks to relieve you from some parts of the job, but to do that you
also need to develop adapters _and_ tediously, boilerplatishly, apply
them to every object from one framework that's going over to the other,
etc. Basically, you risk ending up with very *thick glue* (cfr Eric
Raymond's excellent "The Art of Unix Programming", great book also
available online for free) -- throughout your application, there will be
knowledge about the details of all frameworks you're using, spread in
thick layers of glue.
Now, back to Python world. In many situations, there is no need for
adaptation; the object itself supports the protocol, and can be
supplied directly. But there are situations when the object itself
can't be immediately used; it has to be adapted, or prepared, to
support the protocol. Another situation is when an object is passed to
a routine that *doesn't* support the required protocol; this is an
error, that can be catched by the adapt() framework in a superficially
similar but fundamentally different approach from type checking (and
that's whats Alex has been pointing out).
Oh yes, VERY different. Let me try an analogy...
A policeman's job is to ensure you respect the law. He does that by
trying to catch you violating the law, and punishing you for that.
A civics teacher's job is to ensure you respect the law. He does that
by teaching you the law, explaining its rationale, engaging you in
discussion to find instances where your spontaneous behavior might
violate the law, and working out together with you how to adapt your
behavior and your instincts so that the law gets respects.
Type checking is a policeman. Adaptation is a civics teacher.
The adapt protocol (as presented on PEP246 -
http://www.python.org/peps/pep-0246.html) defines a very flexible
framework to adapt one object to a protocol. The result of the
....and yet the text of PEP 246 is still missing the specs about
registering "third party adapters". Phil Eby's PyProtocols is much
better that way!!! (I promise I'll do something about PEP 246 updating:
just as soon as I'm done with the 2nd ed of the cookbook....!!!!).
adaptation (if possible) is an object that is guaranteed to support
the protocol. So, using adapt(), we can write code like this:
def myfunc(obj):
for item in adapt(obj, Iterable):
...
Hmmm, yes, we can. It's a standard protocol so not the best of
examples, but still, it may be meaningful.
Finally, one may be wondering, is there any situation when an object
needs to be "adapted"? Why don't just check for the availability of
the interface? There are many reasons to use the adapt framework. The
protocol checking is one of the reasons -- it allows errors to be
catched much earlier, and at a better location. Another possible
reason is that complex objects may support several protocols, and
there may be name clashes between some of the methods. One such
situation is when an object support different *versions* of the same
protocol. All versions have the same method names, but semantics may
differ slightly. The adapt() call can build a new object with the
correct method names and signatures, for each protocol or version
supported by the object. Finally, the adaptation method can optionally
build an opaque "proxy" object, that hides details of the original
methods signature, and it's thus safer to pass around.
The main motivation I'd give is that different frameworks not knowing
about each other may define [1] what the object supplies and [2] what
the object is REQUIRED to supply -- there are often discrepancies, and
an adapter in-between is gonna be required. With 246 (once suitably
updated;-) we can write the adapter ONCE, register it in a suitable
global registry, and 'adapt' will just find it. Oh bliss -- as long as
adapt DOES get called all over the place!-)
Well, that's a broad overview of the subject. There is a lot of stuff
to learn, and using adaptation properly is something that takes some
time. Hope it helps.
My compliments for your excellent presentation! I hope my enrichment of
it may have proved useful rather than distracting....
Alex