I'm not done yet... I've just noticed that in the Python world, it is
probably *not* true, actually, that protocols are just multimethods that
dispatch on type, because of multiple inheritance. At least, in my
understanding, I expect protocols to follow the MRO, whereas
multimethods have no reason to do so (special-casing multimethods that
dispatch on py/type would be very ugly indeed); moreover the MRO cannot
be "simulated" using clojure's ad-hoc hierarchies. Let's say for
example that we have
# assume this implements a foo protocolfn using a foo method in the
# class:
@protocol.extends(Foo)
class A(object):
def foo(self): pass
@protocol.extends(Foo)
class B(object):
def foo(self): pass
class C(A, B): pass
class D(B, A): pass
(foo (C)) should call foo_A, but (foo (D)) should call foo_B, so neither
(prefer-method foo A B) not (prefer-method foo B A) is going to help us.
Also, protocols should probably also take care of abstract methods:
@protocol.extends(Foo)
class A(object)
__metaclass__ = ABCMeta
@abstractmethod
def foo(self): pass
class B(A):
def foo(self): return 42
(foo (B)) should return 42. Indeed, for example, ASeq extends the ISeq
protocol, but does not define first and next, so (first foo) (when foo is
an ASeq) should call type(foo).seq (modulo Pythonic complications
regarding __getattribute__ etc etc.), and we don't really want to
repeat, for each subclass of ASeq, that it extends ISeq.
(This is actually easy to do, and already implemented in my code:
basically, if protocolfn.dispatchtable[type(args[0])] is a function fn
that is an abstract method (i.e., that sets __isabstractmethod__ to
True), then instead of calling it on the args, call getattr(args[0],
fn.__name__) on the args.)
Anyways, all this probably means that we're back to (mostly) separate
implementations of multimethods and protocolfns.
Antony
PS: still on the issue of protocols for special methods, probably we
want to be able to still specify an associated protocolfn:
(defprotocol Foo
(__bar__ ^:real-method [self]) ; don't create a protocolfn
(__baz__ ^{:real-method baz} [self])) ; create a baz protocolfn