Why is Python popular, while Lisp and Scheme aren't?

Tim Hochberg tim.hochberg at ieee.org
Thu Nov 21 13:08:52 EST 2002


"Alex Martelli" <aleax at aleax.it> wrote in message
news:5r6D9.50977$Yw.2347638 at news2.tin.it...
> Patrick W wrote:
>
> > Alex Martelli <aleax at aleax.it> writes:

[Nice neat solution for rock-paper-scissors]

[More complicated solution for more extensible rock-paper-scissors]

> so I could somebeat.addpair(aclass, another) to mean than an instance
> of aclass 'beats' one of another unconditionally (when evaluated by
> the criteria in somebeat), or somebeat.addpair(Ba, Bo, pp) to mean
> that for instances of Ba and Bo the 'beating' or otherwise is to be
> decided by calling pp (which must be callable, to mean that:-) with
> the instances as arguments.  And once again that's a small further
> notch in complication which I'd be paying for a flexibility gain.
>
> So, I do miss multiple dispatching, which would let me avoid this
> infrastructure building just like I can avoid it in the more common
> single dispatch case.
>
> But I don't miss it ENOUGH to wish that Python had macros (shudder)...:-).
> Not by a long shot, in fact...

The interesting thing is that as far as I know, no one has put together a
module to simulate multiple dispatch. Or if they have, they haven't pushed
it very hard. I don't think it would be all that hard really, just a tad
more complicated than what you had above. Something similar to what I have
below would probably work, although the performance might not be the best.
Once you have a Dispatcher class (like the one below only better
presumably), writing multiple dispatch functions would be a piece of cake.

-tim


# Rock-Paper-Scissors as an excuse to simulate multiple dispatch.


def argsmatchsig(args, sig):
    if len(args) != len(sig):
        return 0
    for a, s in zip(args, sig):
        if not isinstance(a, s):
            return 0
    return 1

class Dispatcher:
    def __init__(self, table=()):
        self.table = list(table)
    def __call__(self, *args):
        """Try rules in order, calling the first matching rule"""
        for rule, func in self.table:
            if argsmatchsig(args, rule):
                return func(*args)
        raise TypeError("%s instance cannot be called with args types %s" %
                            (self.__class__.__name__,
                            tuple([type(x) for x in args])))
    def add_rule(self, sig, func):
        """Add new rule -- the new rule is tried before all existing
rules"""
        self.table.insert(0, (sig, func))
    def remove_rule(self, sig):
        """Remove the first rule with the given signature."""
        for s, f in self.table:
            if s == sig:
                self.table.remove((s, f))

class Thing(object): pass
class Rock(Thing): pass
class Paper(Thing): pass
class Scissors(Thing): pass
rock, paper, scissors = Rock(), Paper(), Scissors()

def return_true(*args):
    return 1
def return_false(*args):
    return 0

beats = Dispatcher([((Rock, Scissors), return_true),
                    ((Scissors, Paper), return_true),
                    ((Paper, Rock), return_true),
                    ((Thing, Thing), return_false)])


print beats(rock, scissors) # prints 1
print beats(paper, scissors) # prints 0

class Fire(Thing): pass
fire = Fire()

beats.add_rule((Fire, Thing), lambda a, b : "Fire always wins!")

print beats(fire, rock) # prints "Fire always wins!"
print beats(fire, paper) # prints "Fire always wins!"
print beats(scissors, rock) # prints 0

beats.remove_rule((Fire, Thing))

print beats(fire, rock) # prints 0
print beats(fire, paper) # prints 0
print beats(scissors, rock) # prints 0

print beats(scissors, "") # Raises a TypeError





More information about the Python-list mailing list