Hello everybody,
I just found out a neat way to do simple multimethods in Python, so I thought
I'd share it with you :-)
The following is only for dispatching over two arguments. The idea is to use
a pair(a,b) object that is just like a tuple (a,b), but with methods. Any
(regular Python) method that you put in the class of pair(a,b) works like a
multimethod dispatching on both a and b. The metaclass hack is only a
syntactic convenience.
I don't know if this is relevant to PyPy. It's probably one of the shortest
multimethod implementations for Python, whereas PyPy's is probably one of the
longest :-)
Armin
# ______________________________________________
class extendabletype(type):
"""A type with a syntax trick: 'class __extend__(t)' actually extends
the definition of 't' instead of creating a new subclass."""
def __new__(cls, name, bases, dict):
if name == '__extend__':
return bases[0] # return existing base
else:
return type.__new__(cls, name, bases, dict)
def __init__(self, name, bases, dict):
if name == '__extend__':
for key, value in dict.items():
setattr(self, key, value)
def pair(a, b):
"""Return a pair object."""
tp = typeofpair(a.__class__, b.__class__)
return tp((a, b)) # tp is a subclass of tuple
pairtypecache = {}
def typeofpair(cls1, cls2):
"""type(pair(a,b)) is typeofpair(a.__class__, b.__class__)."""
try:
pair = pairtypecache[cls1, cls2]
except KeyError:
name = 'typeofpair(%s, %s)' % (cls1.__name__, cls2.__name__)
bases1 = [typeofpair(base1, cls2) for base1 in cls1.__bases__]
bases2 = [typeofpair(cls1, base2) for base2 in cls2.__bases__]
bases = tuple(bases1 + bases2) or (tuple,) # 'tuple': ultimate base
pair = pairtypecache[cls1, cls2] = extendabletype(name, bases, {})
return pair
# ____________________________________________________________
class A(object):
pass
class B(object):
pass
class C(A):
pass
class D(B):
pass
a = A()
b = B()
c = C()
d = D()
assert isinstance(pair(a,b), typeofpair(A, B))
class __extend__(typeofpair(A, B)):
def add((a, b)):
print "a+b"
def sub((a, b)):
print "a-b"
class __extend__(typeofpair(C, B)):
def add((c, b)):
print "c+b"
# alternate less magic syntax
def sub((a, d)):
print "a-d"
typeofpair(A, D).sub = sub
pair(a,b).add()
pair(a,b).sub()
pair(c,d).add()
pair(c,d).sub()