[Python-Dev] PEP 246: lossless and stateless

Glyph Lefkowitz glyph at divmod.com
Sat Jan 15 01:02:52 CET 2005


On Fri, 2005-01-14 at 10:07 -0500, Phillip J. Eby wrote: 

> Maybe I'm missing something, but for those interfaces, isn't it okay to 
> keep the state in the *adapted* object here?  In other words, if PointPen 
> just added some private attributes to store the extra data?

I have been following this discussion with quite a lot of interest, and
I have to confess that a lot of what's being discussed is confusing me.
I use stateful adapters quite a bit - Twisted has long had a concept of
"sticky" adapters (they are called "persistent" in Twisted, but I think
I prefer "sticky").  Sometimes my persistent adapters are sticky,
sometimes not.  Just's example of iter() as an adaptation is a good
example of a non-sticky stateful adaptation, but this example I found
interesting, because it seems that the value-judgement of stateless
adapters as "good" is distorting design practices to make other
mistakes, just to remove state from adapters.  I can't understand why
PJE thinks - and why there seems to be a general consensus emerging -
that stateless adapters are intrinsically better.

For the sake of argument, let's say that SegmentPen is a C type, which
does not have a __dict__, and that PointPen is a Python adapter for it,
in a different project.

Now, we have nowhere to hide PointPen's state on SegmentPen - and why
were we trying to in the first place?  It's a horrible breach of
encapsulation.  The whole *point* of adapters is to convert between
*different* interfaces, not merely to rename methods on the same
interface, or to add extra methods that work on the same data.  To me,
"different interfaces" means that the actual meaning of the operations
is different - sometimes subtly, sometimes dramatically.  There has to
be enough information in one interface to get started on the
implementation of another, but the fact that such information is
necessary doesn't mean it is sufficient.  It doesn't mean that there is
enough information in the original object to provide a complete
implementation of a different interface.

If there were enough information, why not just implement all of your
interfaces on the original class?  In the case of our hypothetical
cSegmentPen, we *already* have to modify the implementation of the
original class to satisfy the needs of a "stateless" adapter.  When
you're modifying cSegmentPen, why not just add the methods that you
wanted in the first place?

Here's another example: I have a business logic class which lives in an
object database, typically used for a web application.  I convert this
into a desktop application.  Now, I want to adapt IBusinessThunk to
IGtkUIPlug.  In the process of doing so, I have to create a GTK widget,
loaded out of some sort of resource file, and put it on the screen.  I
have to register event handlers which are associated with that adapter.

The IBusinessThunk interface doesn't specify a __dict__ attribute as
part of the interface, or the ability to set arbitrary attributes.  And
nor should it!  It is stored in an indexed database where every
attribute has to be declared, maybe, or perhaps it uses Pickle and
sticking a GTK widget into its representation would make it
un-pickleable.  Maybe it's using an O/R mapper which loses state that is
not explicitly declared or explicitly touch()ed.  There are a variety of
problems which using it in this unsupported way might create, but as the
implementor of a IGtkUIPlug, I should be concerned *only* with what
IBusinessThunk provides, which is .embezzle()
and .checkFundsAvailable().  I am not writing an adapter from
DBBusinessThunkImpl, after all, and perhaps I am receiving a test
implementation that works entirely differently.

This example gets to the heart of what makes interfaces useful to me -
model/view separation.  Although one might be hard pressed to call some
of the things I use adaptation for "views", the idea of mediated access
from a user, or from network protocol, or from some internal code acting
on behalf of a user is the overwhelming majority of my use-cases.

Most of the other use-cases I can think of are like the one James
mentions, where we really are using adaptation to shuffle around some
method names and provide simple glossing over totally isomorphic
functionality to provide backwards (or sideways, in the case of
almost-identical libraries provided on different platforms or
environments) compatibility.

For these reasons I would vastly prefer it if transitivity were declared
as a property of the *adaptation*, not of the adapter or the registry or
to be inferred from various vaguely-defined properties like
"losslessness" or "statelessness".  I am also concerned about any
proposal which introduces transitivity-based errors at adaptation time
rather than at registration time, because by then it is definitely too
late to do anything about it.

I wish I had a better suggestion, but I'm still struggling through the
rest of the thread :).



More information about the Python-Dev mailing list