Discussion: new operators for numerical computation

Neel Krishnaswami neelk at brick.cswv.com
Sat Jul 22 20:52:29 EDT 2000


Aahz Maruch <aahz at netcom.com> wrote:
> In article <slrn8nj9ul.6bu.neelk at brick.cswv.com>,
> Neel Krishnaswami <neelk at alum.mit.edu> wrote:
> >
> > My proposal would be to replace it with multimethods, because that's 
> > the best way of solving the binary method problem in an
> > OO language. 
> 
> Explain this in plain English, please (or better yet, plain Python), for
> those of us who dropped out before getting our CS degrees.

Oops, sorry. I've been doing a lot of Dylan programming lately, and
that has multimethods in the language, so I forgot that this isn't
normal.

So multimethods. In ordinary OO programming, you can think of a method
as a set of functions, and the function body that's chosen is based on
the runtime type of the first argument.

Eg, take two class 

  class Foo:
      def frob(self, arg):
          return "foo" + arg
  
  class Bar(Foo):
      def frob(self, arg):
          return "bar" + arg

  class Baz(Foo):
      def frob(self):
          return "baz" + arg

So when you have an object x, the call x.frob() will return "foo",
"bar", or "baz" based on the runtime type of the self argument.
However, the other arguments are just passed on to the function
called, and have no impact on which method is chosen.

Now, binary methods. A lot of mathematical operations (like +, *, -,
/) are functions with two arguments, and the result should depend on
the type of *both* arguments. You want to call different code to add
(<integer>,<integer>) than (<integer>,<float>) or (<float>,<float>).

IOW, you want to be able to do something like (pseudocode follows):

  define method __mul__(x :: <integer>, y :: <integer>)
    ...add two integers
  end method
  
  define method __mul__(x :: <float>, y :: <float>)
    ...add two floats
  end method
  
  define method __mul__(x :: <integer>, y :: <float>)
     ...add an integer and a float
  end method
  
  define method __mul__(x :: <float>, y :: <integer>)
     ...add a float to an integer
  end method

and then at runtime have the right __mul__ method be picked on the
arguments. The Numeric module makes this obvious, because it contains
a lot of types that implement binary methods in their interface.

I first ran into this when I tried to implement a sparse matrix
class. At that time, the Numpy distribution had a Matrix class that
implemented regular matrix multiplication with '*', and I wanted to
write another class SparseMatrix that would do the right thing
whenever it was multiplied with a Matrix.

This was impossible in regular Python, because an expression like
"matrix * sparse" would always yield an error because Matrix.__mul__
couldn't cope with the multiplication, and try as I might I couldn't
get the __rmul__ hack to work consistently.

Multimethods solve this so elegantly it's hard to see that there was
once a difficulty. Exactly the types that have defined behavior work,
and the rest raise an intelligent error.

The Smalltalk numeric classes implement double dispatch manually for
the Number classes, though this makes it harder to implement
additional number types.


Neel



More information about the Python-list mailing list