[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::
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::
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/)