From: "Jörg W Mittag" Date: 2008-11-30T03:00:03+09:00 Subject: Re: Ruby's duck typing Robert Dober wrote: > On Fri, Nov 28, 2008 at 10:54 PM, Brian Adkins wrote: >> [Message Passing vs. Multimethods] > Sure agree we do not have to prove that Ruby is powerful enough :) and > the link you were presenting is a little confusing to me. Here's my little attempt at explaining multimethods / multiple dispatch. In Ruby and most other single-dispatch OOP languages, methods "belong" to objects, or, in the case of class-based OO languages like Ruby, to classes of which the objects are instances. So, we generally send a message like this: receiver.message(param, anotherparam) However, we could just as well use a procedure instead of a method and call it like this: message(receiver, param, anotherparam) All the information is still there, the method body has access to all the same parameters as it had before. The only difference is that in the method example, the receiver parameter was passed implicitly into the method body and was magically accessible via the special variable keyword "self", where in the procedure example it was passed explicitly. There is one important difference however, and it is one of the pillars of OO: polymorphism. The way that procedures are usually implemented, there can only ever be one procedure with a specific name. With methods however, there can be many methods with the same name, and which method to execute gets determined *at runtime* based on the type (or class) of the special receiver parameter. This is polymorphism, or, more precisely single-dispatch runtime subtype polymorphism. This is where multimethods come in. A multimethod is a procedure like above. *But* there can be many procedures with the same name! *And* which one to execute is determined at runtime, not only by looking at the types of the *first* parameter (like in single-dispatch OO), but by looking at the types of *all* parameters! In that sense, multimethods are more powerful than methods: you can write a multimethod which is only polymorphic in its first parameter, which is exactly the same as with a method. But you can also write a multimethod which is polymorphic in more of its parameters, which you cannot do with methods. You can *fake* it, though: the Visitor pattern is basically an implementation of double-dispatch (i.e. polymorphism on *two* parameters instead of one) for single-dispatch languages. Ruby's coercion protocol is another. Here is a simple example how that might work in Ruby. Remember the coercion example in the Pickaxe book with the Roman numerals? Basically, the problem is this: when I call 'IV'.to_roman + 3 that works, but 3 + 'IV'.to_roman doesn't. Why? Because my Roman class knows about Fixnums but Ruby's builtin Fixnum class doesn't know about Roman numerals. To deal with this, Ruby has this coercion protocol. In this case, Fixnum#+ calls 'IV'.to_roman.coerce(3), which in turn returns something like [3, 4] which can then be used to retry the original operation. With multimethods, all of this would be unnecessary. The "+" method would not be defined inside of Fixnum and Roman (and Float and String and Array and ...) but instead as a multimethod: # This would be part of Ruby Core: defmulti +(a, b) def +(Fixnum f1, Fixnum f2) # do stuff end # This would be part of your library: def +(Roman r, Numeric n) r.to_int + n end def +(Numeric n, Roman r) n + r.to_int end Basically, what multimethods do, is to free polymorphism from the tyranny of the receiver. We can go one step further, however: in CLOS multimethods can not only dispatch on the *type* of all of their arguments, but also on the *value* of all of their arguments. This enables something like this classic example: defmulti factorial(n) def factorial(Integer n) return n * factorial(n - 1) end def factorial(0) return 1 end In all of these examples, the dispatch function became more and more powerful: in procedures, there is no dispatch function. In methods, the dispatch function is basically a simple switch on the type of the first parameter (although not *that* simple: it has to follow the inheritance hierarchy, of course). In simple multimethods, the dispatch function switches on the types of *all* arguments. In CLOS, the dispatch function switches on the types *and values* of all arguments. However, in all of these cases, the dispatch function is a fixed part of the language implementation. So, Clojure takes the obvious next step: when declaring a multimethod, you can provide *your own* dispatch function and you can do whatever you want with the arguments. Switch on the first, switch on all, switch on the sum of the 7th and 42nd, switch on the phase of the moon ... In Ruby, that would look something like this (stolen from the Clojure website): defmulti encounter(animal1, animal2) return animal1.class, animal2.class end def encounter(Lion, Rabbit)(l, r) eat(l, r) end def encounter(Lion, Lion)(l1, l2) fight(l1, l2) end def encounter(Rabbit, Rabbit)(r1, r2) mate(r1, r2) end def encounter(Rabbit, Lion)(r, l) run(r) end And here the ubiquitous enterprise joke: defmulti enterprise_hello_world() return rand <= 0.9, rand <= 0.5 end def enterprise_hello_world(true, true)() puts 'Hello, World!' end def enterprise_hello_world(true, false)() sleep 10 puts 'Hello, World!' end def enterprise_hello_world(false, *)() raise RuntimeError end Note the method body of defmulti: this is *not* the code for the method, this is the dispatch function. Its return value will be matched against the pattern definition in the first set of parantheses. The method definitions have *two* sets of parameters: first the pattern that is matched against the dispatch function, then the formal parameters of the method. Whereas simple multimethods free polymorphism from the tyranny of the first argument, this frees it from the tyranny of the language designer. Anyway, that's my little explanation of multimethods, from someone who understands them as just as badly as you do (-: It's gotten a little long (note to self: start a blog, damnit!), I hope it's not *too* long; and I hope it's helpful. Cheers, jwm