Re: [Python-Dev] Updated Monkey Typing pre-PEP

2005-01-20 Thread Guido van Rossum
[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/)
___
Python-Dev mailing list

Re: [Python-Dev] Updated Monkey Typing pre-PEP

2005-01-20 Thread Mark Russell
On Thu, 2005-01-20 at 11:07, Guido van Rossum wrote:
 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]

Could you not just have a builtin which constructs an interface on the
fly, so you could write:

def foo(f: interface(file.read, file.readlines)): ...

For commonly used subsets of course you'd do something like:

IInputStream = interface(file.read, file.readlines)

def foo(f: IInputStream): ...

I can't see that interface() would need much magic - I would guess you
could implement it in python with ordinary introspection.

Mark Russell

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Updated Monkey Typing pre-PEP

2005-01-20 Thread Phillip J. Eby
At 03:07 AM 1/20/05 -0800, Guido van Rossum wrote:
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.
There are some additional things that it does in this area:
1. Avoids namespace collisions when an object has a method with the same 
name as one in an interface, but which doesn't do the same thing.  (A 
common criticism of duck typing by static typing advocates; i.e. how do you 
know that 'read()' has the same semantics as what this routine expects?)

2. Provides a way to say that you conform, without writing a custom 
__conform__ method

3. Syntax for declaring conformance is the same as for adaptation
4. Allows *external* (third-party) code to declare a type's conformance, 
which is important for integrating existing code with code with type 
declarations


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']): ...
FYI, this is similar to the suggestion from Samuele Pedroni that lead to 
PyProtocols having a:

protocols.protocolForType(file, ['read','readlines'])
capability, that implements this idea.  However, the problem with 
implementing it by actually having distinct protocols is that declaring as 
few as seven methods results in 127 different protocol objects with 
conformance relationships to manage.

In practice, I've also personally never used this feature, and probably 
never would unless it had meaning for type declarations.  Also, your 
proposal as shown would be tedious for the declarer compared to just saying 
'file' and letting the chips fall where they may.


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.
In the common case (at least for my code) an adapter class has only one or 
two methods, but the additional code and declarations needed to make it an 
adapter can increase the code size by 20-50%.  Using @like directly on an 
adapting method would result in a more compact expression in the common case.


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.
Yes, it was for per-object (as a) adapter state, rather than per-adapter 
(has a) state, however.  The PEP didn't try to tackle has a adapters at 
all.


Phillip's proposal reduces the amount of boilerplate in this class
somewhat (mostly the constructor and the __getattr__() method),
Actually, it wouldn't implement the __getattr__; a major point of the 
proposal is that when adapting to an interface, you get *only* the 
attributes from the interface, and of those only the ones that the adaptee 
has implementations for.  So, arbitrary __getattr__ doesn't pass down to 
the adapted item.


 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.
The use case for that is that you are writing a package which extends an 
interface IA to create interface IB, and there already exist numerous 
adapters to IA.  As long as IB's additional methods can be defined in terms 
of IA, then you can extend all of those adapters at one stroke.

In other words, external abstract operations are exactly equivalent to 
stateless, lossless, interface-to-interface adapters applied 
transitively.  But the point of the proposal was to avoid having to explain 
to somebody what all those terms mean, while making it easier to do such an 
adaptation correctly and succinctly.

One problem with using concrete adapter classes to full interfaces rather 
than partial interfaces is that it leads to situations like Alex's adapter 
diamond examples, because you 

RE: [Python-Dev] Updated Monkey Typing pre-PEP

2005-01-20 Thread Raymond Hettinger
[Guido van Rossum]
 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)

Instead of a __getattr__ solution, I recommend subclassing from a mixin:

class RichMap(SomePartialMapping, UserDict.DictMixin): pass

class RichFile(SomePartialFileClass, Mixins.FileMixin): pass



Raymond

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Updated Monkey Typing pre-PEP

2005-01-20 Thread Guido van Rossum
Phillip, it looks like you're not going to give up. :) I really don't
want to accept your proposal into core Python, but I think you ought
to be able to implement everything you propose as part of PEAK (or
whatever other framework).

Therefore, rather than continuing to argue over the merits of your
proposal, I'd like to focus on what needs to be done so you can
implement it. The basic environment you can assume: an adaptation
module according to PEP 246, type declarations according to my latest
blog (configurable per module or per class by defining __typecheck__,
but defaulting to something conservative that either returns the
original object or raises an exception).

What do you need then?

[My plane is about to leave, gotta run!]

-- 
--Guido van Rossum (home page: http://www.python.org/~guido/)
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com