I was pondering the other day, and an idea regarding "method_missing" came to me. I don't want to make this a full-fledged RCR just yet because I want some feedback first, I feel like this is one of those ideas that their may be something blatantly untenable about that I've missed but who knows.
Basically my suggestion is this. Rather than method_missing performing the task of a message when we wish it to dynamically respond to messages, have it return a Proc/Method/something that has a call method. Then the runtime would automatically call this Method/Proc/etc. with the arguments that were originally passed in, assuming of course that thats why method_missing was called. Now why do this you may ask? Well, my rationale is this, we can unify the responses to #method, #respond_to?, etc.
No longer would you have to override respond_to? if you overloaded method_missing, it would call method_missing and see if it got back an object, if it did, it would return true. Similarly with #method, it could return the object returned by method_missing if the method did not already exist.
Pros:
- No more worrying about your custom dispatching in potentially 3 different places, you just write the code once.
Cons:
- Increased complexity of method calling semantics
- Not backwards-compatible at all. (This is a biggee).
So any thoughts? Anything wrong with this that I missed?
And just in case I've been unclear, an example:
% cat example.rb
class Example
def method_missing(name, *args)
lambda { puts "Method #{name} called." }
end
end
example = Example.new
example.hola
if example.respond_to?(:hello)
puts "Example knows how to say hello"
end
% ruby example.rb
Method hola called.
Example knows how to say hello
Pros:
- No more worrying about your custom dispatching in potentially 3
different places, you just write the code once.
Cons:
- Increased complexity of method calling semantics
- Not backwards-compatible at all. (This is a biggee).
So any thoughts? Anything wrong with this that I missed?
Hmmmm ... an interesting idea. It is possible to provide this in a
backwards compatible fashion too. Create a new method (say
"replacement_lambda") that returns nil for Object and rewrite
method_missing and respond_to? to use check for a replacement_lambda.
If one is not found, then just perform the original logic.
Somethind like this ...
···
-------------------------------------
module Kernel
def replacement_lambda(sym)
nil
end
alias original_method_missing method_missing
def method_missing(sym, *args, &block)
if lamb = replacement_lambda(sym)
lamb.call(sym, args, block)
else
original_method_missing(sym, args, block)
end
end
alias original_respond_to? respond_to?
def respond_to?(sym)
replacement_lambda(sym) || original_respond_to?(sym)
end
end
-------------------------------------
I don't particularly like the name "replacement_lambda", and I'm still
deciding if I like the extra complexity or not. But an interesting idea
nevertheless.
"Jim Weirich" <jim@weirichhouse.org> wrote in message
Logan Capaldo wrote:
Pros:
- No more worrying about your custom dispatching in potentially 3
different places, you just write the code once.
Cons:
- Increased complexity of method calling semantics
- Not backwards-compatible at all. (This is a biggee).
So any thoughts? Anything wrong with this that I missed?
Hmmmm ... an interesting idea. It is possible to provide this in a
backwards compatible fashion too.
+1.
Given Jim's way of retaining backward compatibility this would be good.
Which methods are affected? method_missing, respond_to?, method ...
I guess things that enumerate methods (e.g. #methods, #instance_methods)
could not be covered.
Minor suggestion, I would flip that around, since it would probably A) be faster in the common case and B) consistent with current behavior. (If you call a method that something #respond_to?s why are you gonna go through the extra dynamic dispatch.)
Other than that, I like how you managed to keep it backwards compatible and in pure ruby no less.
···
On Jan 4, 2006, at 9:35 AM, Jim Weirich wrote:
def respond_to?(sym)
replacement_lambda(sym) || original_respond_to?(sym)
end
end