[Python-Dev] Updated Monkey Typing pre-PEP

Guido van Rossum gvanrossum at gmail.com
Thu Jan 20 12:07:35 CET 2005


[Phillip J. Eby]
> I've revised the draft today to simplify the terminology, discussing only
> two broad classes of adapters.  Since Clark's pending proposals for PEP 246
> align well with the concept of "extenders" vs. "independent adapters", I've
> refocused my PEP to focus exclusively on adding support for "extenders",
> since PEP 246 already provides everything needed for independent adapters.
> 
> The new draft is here:
> http://peak.telecommunity.com/DevCenter/MonkeyTyping

On the plane to the Amazon.com internal developers conference in
Seattle (a cool crowd BTW) I finally got to read this. I didn't see a
way to attach comments to Phillip's draft, so here's my response. (And
no, it hasn't affected my ideas about optional typing. :)

The Monkey Typing proposal is trying to do too much, I believe. There
are two or three separate problem, and I think it would be better to
deal with each separately.

The first problem is what I'd call incomplete duck typing. There is a
function that takes a sequence argument, and you have an object that
partially implements the sequence protocol. What do you do? In current
Python, you just pass the object and pray -- if the function only uses
the methods that your object implements, it works, otherwise you'll
get a relatively clean AttributeError (e.g. "Foo instance has no
attribute '__setitem__'").

Phillip worries that solving this with interfaces would cause a
proliferation of "partial sequence" interfaces representing the needs
of various libraries. Part of his proposal comes down to having a way
to declare that some class C implements some interface I, even if C
doesn't implement all of I's methods (as long as implements at least
one). I like having this ability, but I think this fits in the
existing proposals for declaring interface conformance: there's no
reason why C couldn't have a __conform__ method that claims it
conforms to I even if it doesn't implement all methods. Or if you
don't want to modify C, you can do the same thing using the external
adapter registry.

I'd also like to explore ways of creating partial interfaces on the
fly. For example, if we need only the read() and readlines() methods
of the file protocol, maybe we could declare that as follows::

  def foo(f: file['read', 'readlines']): ...

I find the quoting inelegant, so maybe this would be better::

  file[file.read, file.readlines]

Yet another  idea (which places a bigger burden on the typecheck()
function presumed by the type declaration notation, see my blog on
Artima.com) would be to just use a list of the needed methods::

  [file.read, file.readlines]

All this would work better if file weren't a concrete type but an interface.

Now on to the other problems Phillip is trying to solve with his
proposal. He says, sometimes there's a class that has the
functionality that you need, but it's packaged differently. I'm not
happy with his proposal for solving this by declaring various adapting
functions one at a time, and I'd much rather see this done without
adding new machinery or declarations: when you're using adaptation,
just write an adapter class and register it; without adaptation, you
can still write the adapter class and explicitly instantiate it.

I have to admit that I totally lost track of the proposal when it
started to talk about JetPacks. I believe that this is trying to deal
with stateful adapters. I hope that Phillip can write something up
about these separately from all the other issues, maybe then it's
clearer.

There's one other problem that Phillip tries to tackle in his
proposal: how to implement the "rich" version of an interface if all
you've got is a partial implementation (e.g. you might have readline()
but you need readlines()). I think this problem is worthy of a
solution, but I think the solution could be found, again, in a
traditional adapter class. Here's a sketch::

class RichFile:
    def __init__(self, ref):
        self.__ref = ref
        if not hasattr(ref, 'readlines'):
            self.readlines = self.__readlines # Other forms of this
magic are conceivably
    def __readlines(self): # Ignoring the rarely used optional argument
        # It's tempting to use [line for line in self.__ref] here but
that doesn't use readline()
        lines = []
        while True:
            line = self.__ref.readline()
            if not line:
                break
            lines.append(line)
        return lines
    def __getattr__(self, name): # Delegate all other attributes to
the underlying object
        return getattr(self.__ref, name)

Phillip's proposal reduces the amount of boilerplate in this class
somewhat (mostly the constructor and the __getattr__() method), but
apart from that it doesn't really seem to do a lot except let you put
pieces of the adapter in different places, which doesn't strike me as
such a great idea.

-- 
--Guido van Rossum (home page: http://www.python.org/~guido/)


More information about the Python-Dev mailing list