[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