[Python-3000] Adaption & generic functions [was Generic functions]

Phillip J. Eby pje at telecommunity.com
Sun Apr 9 17:57:35 CEST 2006


At 12:58 PM 4/7/2006 -0700, "Eli Stevens (WG.c)" <listsub at wickedgrey.com> 
wrote:
>Guido van Rossum wrote:
> > Did you forget duck typing? Something can be a sequence without
> > subclassing a common base class.
>
></lurk>
>
>I'm curious what effect overloaded functions will have on duck typing; a
>Something can act like a list, but from my understanding of the
>discussion* it won't end up matching:
>
>@pprint.register(list)
>def pprint_list(obj):
>      pass

Note that this doesn't work *now* with pprint, even if you actually 
*subclass* list.  At least with this approach, subclassing list would work 
automatically.  :)


>It seems to me that now to get a duck-typed list, not only do you have
>to implement all of special methods that define "listy-ness," you also
>have to find the overloaded functions that are specialized for lists,
>and register your own implementation (or an implementation specialized
>for lists, if appropriate).

It's true that generic functions do not by themselves resolve the question 
of what "listy-ness" is.  Your comment does highlight the idea, however, 
that it would be useful to be able to use duck typing to target generic 
function methods.  For example, to say that "this method is usable against 
any object that can have these other functions called on it".  A kind of 
mapping from an operation to dependent operations, such that you can define 
a generic "operator.getslice" method for any object that can be used with 
the "operator.getitem" generic function.

This sort of goes back to the "monkey typing" proposal I made early last 
year in response to the last big python-dev debate on adaptation.  Monkey 
typing is in fact just generic functions combined with a kind of "universal 
adapter" type, that allows you to treat generic functions as methods of the 
adapter object, rather than calling them as functions.

In fact, this is very close to what the Haskell programming language calls 
"typeclasses".  A typeclass in Haskell is basically a set of generic 
functions that apply to some type.  You declare an "instance" of a 
typeclass by specifying the operations that correspond to the operations in 
the typeclass, which then allows your new type to be used by any code that 
expects the typeclass.

So, in Haskell you could define "listyness" by declaring a typeclass that 
included various listy generic functions (setitem, getitem, insert, 
delitem, and len, perhaps).  Then, to declare your new listy type, you 
would merely need to specify what functions or methods of your type 
corresponded to the "listy" generic functions.  Since other functions' 
methods can be defined in terms of listyness, there's no need for you to 
register for all possible things everywhere that take listy things.

You may be wondering why typeclasses aren't the same thing as protocols or 
interfaces.  The key difference is that a typeclass is defined in terms of 
*independent operations*.  A typeclass is basically a collection of related 
generic functions, *not* a specification of a type's behavior.  This means 
that operations can be *shared* between typeclasses.

This is crucial, I think, to Python, where we have some very "fat" 
interfaces that can be defined using different method subsets.  For 
example, "read file" vs. "write file":  both have a "close()" 
method.  Using traditional interface concepts found in Python and Java, you 
would have to declare two different interfaces, or create a "read-write" 
file interface in order to handle this.  With typeclasses, however, the 
notion of "close()" can be independent, and you can assemble new 
typeclasses as needed by simply referring to the operations needed.

And -- the most crucial point -- when somebody defines a new typeclass 
composed only of existing operations that your type can support, then your 
type gets to play without anybody making any additional declarations at all.

This to me seems more Pythonic than traditional interfaces, as it allows 
something that's essentially just duck typing, but based on *objects* (the 
generic functions) rather than *names*.  Where an interface is a namespace 
that defines operations, a typeclass is defined by the operations that 
comprise it.  Thus, a typeclass for "duck" is just shorthand for some set 
of behaviors like walking and quacking.



More information about the Python-3000 mailing list