3

I wonder if the concept of multiple dispatch (that is, built-in support, as if the dynamic dispatch of virtual methods is extended to the method's arguments as well) should be included in an object-oriented language if its impact on performance would be negligible.

Problem

Consider the following scenario: I have a -- not necessarily flat -- class hierarchy containing types of animals. At different locations in my code, I want to perform some actions on an animal object. I do not care, nor can I control, how this object reference is obtained. I might encounter it by traversing a list of animals, or it might be given to me as one of a method's arguments. The action I want to perform should be specialized depending on the runtime type of the given animal. Examples of such actions would be:

  • Construct a view-model for the animal in order to present it in the GUI.
  • Construct a data object (to later store into the DB) representing this type of animal.
  • Feed the animal with some food, but give different kinds of food depending on the type of the animal (what is more healthy for it)

All of these examples operate on the public API of an animal object, but what they do is not the animal's own business, and therefore cannot be put into the animal itself.

Solutions

One "solution" would be to perform type checks. But this approach is error-prone and uses reflective features, which (in my opinion) is almost always an indication of bad design. Types should be a compile-time concept only.

Another solution would be to "abuse" (sort of) the visitor pattern to mimic double dispatch. But this would require that I change my animals to accept a visitor.

I am sure there are other approaches. Also, the problem of extension should be addressed: If new types of animals join the party, how many code locations need to be adapted, and how can I find them reliably?

The Question

So, in the light of these requirements, shouldn't multiple dispatch be an integral part of any well-designed object-oriented language?
Isn't it natural to make external (not just internal) actions dependent on the dynamic type of a given object?

Best regards!

domin
  • 1,192
  • 1
  • 7
  • 28
  • The Visitor pattern is pattern-speak for double-dispatch. No mimicry or abuse is involved in using the Visitor pattern to obtain double-dispatch. – John Bollinger May 26 '15 at 18:23
  • @JohnBollinger When visitors are described/motivated, it is often talked about the traversal of an object structure. In the examples given, there is neither an object structure nor any kind of traversal involved. One could argue that I am visiting a single object which is a degenerated form of object structure. :) – domin May 26 '15 at 18:38
  • that traversing an object graph is a common example of a *use* for the Visitor pattern in no way makes it part of the *definition* of that pattern. – John Bollinger May 26 '15 at 18:55
  • I do not know the *official* definition of the pattern, if such a thing even exists. I just saw it described in terms of these use cases. As long as the verb "visiting" makes sense, if might not be an abuse. Agreed. – domin May 26 '15 at 19:12

3 Answers3

0

One "solution" would be to perform type checks. But this approach is error-prone and uses reflective features, which (in my opinion) is almost always an indication of bad design. Types should be a compile-time concept only.

You're wrong. All uses of virtual functions, virtual inheritance, and such things involve reflective features and dynamic types. The ability to defer typing until runtime when you need to is absolutely critical and is inherent in even the most basic formulation of the situation you're in, which literally cannot even arise without the use of dynamic types. You even describe your problem as wanting to do different things depending on.. the dynamic type. After all, if there is no dynamic typing, why would you need to do things differently? You already know the concrete final type.

Of course, a bit of run-time typing can handle the problem you got yourself into with run-time typing.

Simply build a dictionary/hash table from type to function. You can add entries to this structure dynamically for any dynamically linked derived types, it's a nice O(1) to look up into, and requires no internal support.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • At runtime, an object does not have to be aware of its own type (or even the name of its type), it simply *acts* by making the reception and handling of a message (i.e. a method call) part of its behavior. That of course does not rule out the fact that the object *is* of some type. You are a human by acting like one, not by knowing that you are one. – domin May 26 '15 at 18:45
  • Regarding your proposal: A dictionary is a dynamic structure. So the compiler cannot give me any kinds of guarantees. It is basically just an alternative to a huge type-switch, with the unneeded (and potentially dangerous) possibility to change at runtime. – domin May 26 '15 at 18:51
0

You are suggesting dynamic dispatching based on method name / signature combined with runtime actual argument types. I think you're crazy.

So, in the light of these requirements, shouldn't multiple dispatch be an integral part of any well-designed object-oriented language?

That there are problems for which the availability of the kind of dispatch strategy you envision would simplify coding is a weak argument for such dispatch being built into any given language, much less every OO language.

Isn't it natural to make external (not just internal) actions dependent on the dynamic type of a given object?

Perhaps, but not everything that seems "natural" is in fact a good idea. Clothes are not natural, for instance, but see what happens if you try going around in public without (somewhere other than Berkeley, anyway).

Some languages already have static dispatch based on argument types, more conventionally called "overloading". Dynamic dispatch based on argument types, on the other hand, is a real mess if there is more than one argument to be considered, and it cannot help but be slow(er). Today's popular OO languages provide for you to perform double dispatch where it is wanted, without the overhead of supporting it in the vast majority of places where you don't want it.

Furthermore, although implementing double-dispatch does present maintenance issues arising from tight coupling between separate components, there are coding strategies that can help keep that manageable. It is anyway unclear to what extent having argument-based multiple dispatch built in to a given language would actually help with that problem.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • I agree that with more than one argument, It can become pretty messy (method priorities and such), and that performance suffers. But still: What coding strategy would help here? And what would actually be a "good" solution? And why? – domin May 26 '15 at 18:59
  • "What coding strategy would help here?" The Visitor pattern benefits from being designed around overloaded `visit` methods (as opposed to name-per-visited-type methods), with one serving as a catch-all. That makes it a lot easier to integrate new visitable types, at least in Java. In C++ it doesn't matter, because you need to recompile everything whenever you make any change anyway. In duck-typed languages, you don't even need to worry about it. – John Bollinger May 26 '15 at 19:07
  • "What would actually be a 'good' solution? And why?" It depends a lot on the specifics of the problem and the context in which a solution is required. In my experience, it's not a problem that arises very often. – John Bollinger May 26 '15 at 19:12
0

If one restricts oneself to the situation where knowledge of how an object of type X should fnorble an object of type Y must be stored in either class X or class Y, one can have the base type of Y include a method that accepts a reference of X's base type and indicates how much an object knows about how to be fnorbled by the object identified by that reference, as well as a method that asks the Y to have an X fnorble it.

Having done that, one can have X's Fnorble(Y) method start by asking the Y how much it knows about being fnorbled by a particular type of X. If the Y knows more about X than X knows about Y, then X's Fnorble(Y) method should call the Y's BeFnorbledBy(X) method; otherwise, the X should fnorble the Y as best it knows how.

Depending upon how many different kinds of X and Y there are, Y could define BeFnorbledBy overloads methods for different kinds of X, such that when X calls target.BeFnorbledBy(this) it would automatically dispatch directly to a suitable method; such an approach, however, would require every Y to know about every type of X that was "interesting" to anybody whether or not it had any interest that particular type itself.

Note that this approach doesn't accommodate the situation where there might be an outside object of class Z which knows things about how an X should fnorble a Y that neither X nor Y knows directly. That kinds of situation is best handled by having a "rulebook" object where everything that knows about how various kinds of X should fnorble various kinds of Y can tell the rulebook, and code which wants an X to fnorble a Y can ask the rulebook to make that happen. Although languages could provide assistance in cases where rulebooks are singletons, there may be times when it may be useful to have multiple rulebooks. The semantics in those cases are probably best handled by having code use rulebooks directly.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • I guess the matrix-matrix multiplication textbook example pretty much is an instance of your abstract scheme. This is basically a hidden visitor pattern at work. In this case, the coupling is so tight that it really seems to be the most elegant solution. – domin May 26 '15 at 19:07
  • What is "fnorble" – FreelanceConsultant Jun 07 '23 at 08:58
  • @FreelanceConsultant: Place-holder term. I forget where I first saw it used. – supercat Jun 07 '23 at 14:55