Re: Definitions

From: Michael C. Ferguson (mcf_at_augustmail.com)
Date: 10/22/03


Date: Wed, 22 Oct 2003 07:05:07 GMT

On 19 Oct 2003 01:50:59 -0700, rackri@yahoo.it (rackri) wrote:

>can you please help me to understand these concepts:
>- double-dispatch

I suspect this is someone's homework assignment, but whatever. I'll
take a swing at DD, since implementing it is one of my biggest
pet-peeves in modern languages... (Well, PERL 6 will soon have
multimethods... but I still won't be writing code in it so it doesn't
matter).

Here is the basic problem description:

You have a base-class pointer and you want to pass it to a function
which is defined for most (or all) derived classes. Unfortunately,
most languages do static-binding for overloaded function calls, based
on the actual parameter that was passed. Here's some code, which I
think makes more sense than my OO rhetoric...

// Please ignore for a moment that we could actually make
// this work by putting SomeFunction inside the classes.
// That is not the point of the exercise, and I'm trying to
// make this simple and get the idea across at the same time...

class Base { ... };
class Derived : public Base { ... };

void SomeFunction(Base * ptr);
void SomeFunction(Derived * ptr);

// Now, when we do a function call on SomeFunction()
// using a base class pointer to a derived class, we
// don't get what we really expect (well, we don't get
// what *I* expect anyway :P)

Base * b = new Derived();

SomeFunction(b); // <--- This calls SomeFunction(Base) :-(
                 // but we really want to call SomeFunction(Derived)
                 // instead. Sigh... life is unfair.

Most modern languages (C++, Java, C# -- the ones people code in,
anyway) are not polymorphic on method arguments. Languages that do
support that type of polymorphism are said to support "multimethods".
But being polymorphic on more than one arguments can be very useful...
so what do we do? That's where Double Dispatch (aka Multi Dispatch)
comes in.

Double Dispatch is our fun, creative way of getting "the right answer"
that our compiler should have given us from the beginning. Kind of
like function pointers in C, it is a big, dirty hack. Here is
basically how it is accomplished: Since we already have
single-arguement polymorphism (on a method called on a class, such as
class->method() or class.method()), we can use it to hack up something
that is basically the same as multimethods. Hopefully this example
will be more forthcoming than the rhetoric we tend to pick up *after*
understanding the problem (btw, there are many better examples -- for
instance, Visitor pattern in GoF):

// Now I am writing this after having been out drinking, so there
// are probably going to be a few mistakes. I'll try to explain
// everything at the end...

// We could also make Base an interface or a more abstract class, but
// it would just be more code for no reason. This is just an example;
// I am not suggesting you actually code like this...

class Base {
public:
        virtual void ClassAMethod(A a) // someone shoot me for that
        {
                a.ClassABaseMethod(this);
        }
};

class Derived : Base {
public:
        virtual void ClassAMethod(A a)
        {
                a.ClassADerivedMethod(this);
        }
};

class A {
public:
        void MethodOnBasePtr(Base * b)
        {
                b->ClassAMethod(*this);
        }

        void ClassABaseMethod(Base * b) {...};
        void ClassADerivedMethod(Derived * d) {...};
};

// And, the whole reason that this junk is there...

Base * b = new Derived();
A a; // oh, I am going to Programmer Hell now...

a.MethodOnBasePtr(b); // Voila! Now we get what we want
                      // (although we go through much pain for it)

Wow, that is a mess. So what did we really do? Basically, since the
language itself cannot figure out what our method arguments is (in
MethodOnBasePtr() ), we help it out by figuring it out ourselves. When
we make a call to a.MethodOnBasePtr(), all the language will ever know
is that we have a base pointer -- as we learned above, even though the
pointer points to a derived class object, the compiler will still
happily bind the method call to the method with the actual type of the
parameter (the method acting on a Base *).

But, since we have a base pointer, we have something we can do: we can
call a method on that pointer (ClassAMethod()) and it will be
magically resolved to the correct class's member method (in this case,
Base and Derived's ClassAMethod()). Once we're inside the right class
(via the polymorphic method call), we know our type, so we can do a
callback to A with the right method and a properly typed pointer. This
is all to accomplish what we could not do in the first example: be
polymorphic on a method argument.

Obviously, DD introduces a lot of overhead, both in code and in
runtime cost. Every time you add a new derived class of Base, you must
also change class A, or you're breaking your contract with A. On the
other hand, if the language itself could do the polymorphic method
call, we would not have this trouble. We could write a generic
function which would be the "best match" if no other more specific
function existed, and the call would be automatically bound to it
instead. [There may be slight ways around that shortcoming, with a
more abstract base class -- but it is still needlessly ugly.]

Life would be much, much easier if languages would do multimethods, so
I'm not exactly sure why they don't. I guess it may be because very
few programmers are using polymorphism correctly right now anyway, so
why bother with providing anything more complex? It's like feeding
lobster bisque to pigs.

HTH,

-- mcf



Relevant Pages

  • Re: multimethod + multiple inheritance
    ... polymorphic value instead of a pointer to it. ... what I said, no polymorphism without pointer. ... Note that the problem of geometrical explosion of variants is still ... dynamically typed languages (see article "Haskell vs. Erlang, ...
    (comp.object)
  • Re: Question on LSP
    ... mavens weren't strongly influenced by OOA/D abstraction semantics like is-a. ... polymorphism available to OO development, ... /relationship/ that makes LSP substitutability concrete, ... pointer dispatch is the same as polymorphic dispatch. ...
    (comp.object)
  • Re: memory allocation questions (newbie)
    ... I think this means something like "a pointer that is ... prepared to point to a memory location big enough to hold an int." ... I think having multiple exit points is not a good idea. ... experience writing programs in high-level languages. ...
    (comp.lang.c)
  • Re: identity...... Was: The wisdom of the object mentors
    ... At least in C++ 'this' does not return the object's value, but a pointer. ... // Dereference the pointer (gives an object of class Bar) ... things is what OO calls polymorphism. ... Boolean object has no place to keep the class identity. ...
    (comp.object)
  • Re: Static vs Dynamic
    ... The type system in SML has the notion of ``parametric polymorphism'' ... functions that operate on lists in general, ... polymorphism, and for ease of type inference. ... Other languages with similar goals include Objective CAML, ...
    (comp.lang.lisp)