adaptation

Carlos Ribeiro carribeiro at gmail.com
Tue Nov 16 14:27:55 EST 2004


On Tue, 16 Nov 2004 19:45:29 +0100, Alex Martelli <aleaxit at yahoo.com> wrote:
> Carlos Ribeiro <carribeiro at gmail.com> wrote:
>    ...
> > 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.

Nice summary of the difference. 
 
> > 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.

Good example. I have my own take on this: type checking is about being
strict (to the point of intolerance). Adaptation is about being
flexible.

> > 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....!!!!).

That's another thing that I thought about including... but I was
afraid of broadening the scope too much. But at least a pointer to
PyProtocols is needed, if this small intro is to be turned out into a
'what is adaptation' tutorial of sorts.

> > 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.

I thought that it was a good example to show what adapt() does... for
someone who never thought about it before.
 
> > 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!-)

I was not entirely satisfied with this part of my explanation, but I
was afraid of taking too many things at once. If I was to rewrite it
now I would probably restructure it, and include a few considerations
about the case with similar (but slightly and annoyingly different)
frameworks.
 
> > 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
> 
> 
> --
> http://mail.python.org/mailman/listinfo/python-list
> 


-- 
Carlos Ribeiro
Consultoria em Projetos
blog: http://rascunhosrotos.blogspot.com
blog: http://pythonnotes.blogspot.com
mail: carribeiro at gmail.com
mail: carribeiro at yahoo.com



More information about the Python-list mailing list