From: MonkeeSage Date: 2007-12-07T09:30:04+09:00 Subject: Re: Worth an RCR? static_type_check, polymorphic_type_check, quacks_like On Dec 6, 5:16 am, Robert Klemme wrote: > 2007/12/6, MonkeeSage : > > > > > On Dec 5, 7:08 pm, Trans wrote: > > > On Dec 5, 6:48 pm, John Carter wrote: > > > > > Is there another library like this? I would love it if it were just > > > > standard methods on Object. > > > > > I find this very useful and 'require' it into all code I write. Just > > > > helps me mop up those problems that static type checking would have > > > > found for me... > > > > > Heres a typical usage, create an object and initialize it with > > > > stuff. Only much later when you invoke the methods on the object do > > > > you find you initialized it with the wrong stuff. > > > > > Got the parameter order wrong or something. But then the bug isn't on > > > > the backtrace. So put when you initialize your instance variables you > > > > can type check'em like so... > > > > > eg. > > > > class MyObject > > > > def initialize( _name, _drawable, _theme) > > > > @name = _name.static_type_check String > > > > @drawable = _drawable.polymorphic_type_check Drawable > > > > @theme = _theme.quacks_like :border_color, :fill_colour > > > > end > > > > Special methods? Just do: > > > > def initialize( _name, _drawable, _theme) > > > raise ArgumentError unless _name.instance_of?(String) > > > raise ArgumentError unless _drawable.kind_of?(Drawable) > > > raise ArgumentError unless [:border_color, :fill_color].all?{|m| > > > _theme.respond_to?(m)} > > > @name, @drawable, @theme = _name, _drawable, _theme > > > end > > > > Besides, with exception to the 2nd (done for the right reason) this is > > > all generally considered a bad practice. If you want to do it for > > > debugging purposes that's cool --wrap the the check in an 'if $DEBUG'. > > > But the idea behind duck-typing is to NOT do these types of checks. > > > Just let it ride, so we can drop whatever we want into your method -- > > > if our object is "adapted" right then it can work regardless. > > > > Note: I used to think the last made sense. But I've learned over time > > > that it actually is worse --you're assuming those methods dictate some > > > type of behavior, but those methods might not do what you think they > > > do. Use of respond_to? should be generally avoided. > > > > T. > > > Heh. I used to think the opposite about respond_to?, but for a while > > I've been thinking about it as not asking for a promise of behavior, > > but for a promise that it's safe to act like we have a promise of > > behavior. Just like when we use "begin...rescue NoMethodError", we're > > not saying "now I've ensured X object will behave well", we're saying > > "now I can safely pretend like object X implements some interface" The > > only difference *at all* that I see between asking respond_to? and > > using a "begin...rescue NoMethodError" clause, is that one asks for > > permission and the other asks for forgiveness (i.e., the only > > difference is the point in time at which they occur). That said, there > > are problems with method signature validation by respond_to?, as many > > people have pointed out before, e.g., method_missing dispatching with > > variable arity. So, while I would agree that it may not often be a > > good idea, pragmatically, I disagree with the idea that it > > *necessarily* promotes a wrong way of thinking about programming (or > > at least programming in ruby). > > Some more random thoughts about respond_to? > > 1. the point in time may be crucial, i.e. when checking on > initialization the object might actually not yet respond to the method > but logic otherwise guarantees that it does when the method is > actually invoked. > > 2. point in time is not the only difference as you have pointed out > (method_missing). > > 3. the net effect is the same, i.e. if you check with respond_to? you > will have to raise an exception - you will get an exception as well > when you simply call the method. I can't think of an actual use case offhand (heh, that probably speaks for itself), but I've used it before in places where the exceptional case is non-terminal. A contrived example... class B < Array; private :index; end b = B.new unless b.respond_to? :index p "state 1, use #foo" class B; public :index; end end #... if b.respond_to? :index p "state 2, use #index" class B; private :index; end end > For the me disadvantages of respond_to? and type checks by far > outweigh the benefits (assumed increase in robustness or faster bug > finding). > > Kind regards > > robert > > -- > use.inject do |as, often| as.you_can - without end Regards, Jordan