In my first post of this series, I wrote about Objective Caml and it's various features. OCaml is a statically typed language, which differs from today's topic: Dylan. The history of Dylan on Wikipedia suggests that the name is not for the folk musician as I had always assumed, but a concatenation of DYnamic LANguage.
Developed in the early 90s at Apple (of all places), Dylan aimed to be a dynamic language suitable for commercial software. At the time there was a gap in this space that many people were attempting to fill. Python started around then and Perl was well on it's way to version 4, but no language was a clear front runner.
According to Wikipedia, Apple intended to use it for their Newton PDA, but the project didn't quite make it. Other groups got involved, with limited success, and today there are only two Dylan implementations: Gwydion Dylan and Open Dylan. Both versions appear to be maintained by the same group, the Gwydion Dylan Maintainers, with the former version remaining a Dylan-to-C compiler and the latter compiling to native code.
Dylan is like an evolutionary oddity that has survived in the backwoods of Tasmania or the Galapagos Islands. The odds of extinction are certainly greater than wide-spread adoption or success. But it's worth an archaeological inspection.
Dylan and multiple dispatch
This is not unique to Dylan, many languages offer multiple dispatch. CLOS is the most notable, but extensions exist for Java, Python, Perl, and Ruby, among others. Though the Lisp implementation is more worthy of analysis, a lot of average programmers don't know much Lisp. I use Dylan as an example because it's syntax may feel more familiar. Java's extension library, for example, is a little awkward and makes it hard to think outside the usual Java-style object-orientedness.
Multiple dispatch is also referred to as multi-methods or generic functions. The concept is fairly simple. Instead of attaching functionality to a class, you define it generically. I've seen this described as "code separated from storage." Examples from computer game logic make the most sense to me. Consider an 'attack' function used when two game entities fight.
define generic attack (entity1, entity2)
In Dylan syntax, this defines a generic function that takes two required arguments. Now you can define more specific versions:
define method attack (entity1 :: player, entity2 :: monster)
// implement logic for player attacking a monster
end method;
define method attack (entity1 :: monster, entity2 :: monster)
// implement logic for monster attacking a monster
end method;
define method attack (entity1 :: thing, entity2 :: thing)
// implement logic for a thing attacking another thing
end method;
In this snippet there are three classes: player, monster, thing. These are typical objects with their own attributes and methods (Dylan supports the usual class-method relationship in addition to generic functions). The trick is when you call attack and pass it two arguments, the method that runs is dependent on the types of the arguments.
This is completely different than most object-oriented languages where the method that runs is dependent solely on the object that calls it. The essence of multiple dispatch is the execution of methods is determined by the types of all the arguments.
In the game example, imagine if all objects inherit from a class called 'thing.' Monsters and players are subclasses of thing. Other game items could inherit from thing, too, like 'potion' or 'animal'. If your game logic calls attack and passes it a player object and a monster object, Dylan will dispatch on the first method. If the arguments are a player object and an animal object, Dylan will dispatch on the third method because there is nothing more specific than attack (thing, thing). The most specific method wins.
Next method
Multiple dispatch changes the approach to object-oriented programming. In a lot of ways it feels wrong, like we're back to writing GOSUB and destroying the entire purpose of objects. But a deeper look suggests there could be something to it, especially for specific applications (like game programming).
One shade of complexity involves calling "lower-level" methods. In the above game example, suppose you want certain behavior to occur every time a thing attacks another thing. If you call attack (player, monster), only one method executes (the most specific). You can circumvent this behavior using next-methods. Redefine the generic function like so:
define method attack (entity1 :: player, entity2 :: monster)
// logic for player attacking monster
next-method
end method;
Next-method invokes the next most specific method. In this case it would be the third example: attack (thing, thing). This allows for behavior that's more object-oriented in nature, similar to calling super-class methods in "traditional" object-oriented languages. See chapter 6 of the Dylan Reference Manual for more details on method specificity.
Unexplored territory
This appears to me as unexplored territory. You don't see a lot of talk about multiple dispatch and there are few resources to examine. It's typically limited to academia, though I'm surprised not to hear more about it from the game programming world. The effect on performance is something I don't quite understand, either, but there are certainly performance hits.
The multiple dispatch Wikipedia article has a few more examples of implementation and usage in other languages.