Aahz said:
Hrm. I don't recall anything about typeclasses, so my suspicion is that
you were writing something lengthy and above my head. Can you write
something reasonably short about it? (I'm asking partly for your
benefit, because if it makes sense to me, that will likely go a long way
toward making sense to Guido -- we seem to think similarly in certain
ways.)
Think of a typeclass as something "like an interface, more than an
interface" to which a type can easily be "adapted" by a third programmer
even if the two programmers who wrote the type and typeclass were
working separately and with no knowledge of each other -- not too far
from the vaguer idea of "protocol" I support in PEP 246 (which focuses
on the adaptation), except that in Haskell things happen at compile time
while in Python we prefer to avoid the strong distinction between
compile time and runtime.
You may think of a typeclass as akin to an abstract baseclass, because
it's not constrained to only giving the signatures of methods, it can
also supply some default implementations of some methods in terms of
others. Guido blogged in August about interfaces versus ABCs, not
remembering why he had once Pronounced about preferring ABCs, and in his
critique of ABCs he mentions that one weakness of their ability to
provide default implementations is that you have to decide about what is
the most fundamental subset, in whose terms the rest is implemented.
But *typeclasses do away with that need*. Consider (arbitrary
pythonesquoid syntax):
typeclass mapping:
def __getitem__(self, key):
_notthere=[]
result = self.get(key, _notthere)
if result is notthere: raise KeyError
return result
def get(self, key, default):
try: return self[key]
except KeyError: return default
# etc etc
this LOOKS like mutual recursion, but since it's a typeclass it doesn't
mean that: it means __getitem__ may be defined (and then get uses the
provided default implementation unless overridden) OR get may be defined
(and then it's __getitem__ that may use the default implementation
supplied by the the typeclass, or else override it).
When you compile a typeclass you build a directed graph of dependencies
of methods on each other, which may include cycles; when you show how a
type adapts to a typeclass, you build a copy of that graph removing the
dependencies of those methods which do get explicitly implemented (in
the type or in the adapter) -- if the copy at the end of these
compilations still has cycles, or leaves (methods that the typeclass
requires and neither the type nor the adapter supply), then this raises
an exception (incomplete adaptation).
Thus, a typeclass clearly shows the semantics intended for methods that
depend on each other, and conveniently lets you, the adapter's author,
choose what to implement -- the typeclass's author has not been forced
to pick the "most fundamental" methods. ABCs, or extremely handy mixins
such as UserDict.DictMixin, do force a programmer who knows nothing
about the internals of your class (the author of the ABC or mixin) to
pick "most fundamental" methods. Thus, typeclasses are more useful than
ABCs by as much as ABC are more useful than (simply "syntactical")
interfaces -- coupled with adaptation mechanisms, the overall result can
be extremely handy (as any Haskell programmer might confirm).
Alex