From: Patrick May Date: 2004-06-29T09:54:23+09:00 Subject: Re: Is it considered Harmful? Sean, On Monday, June 28, 2004, at 02:04 PM, Sean O'Dell wrote: >> If I call Array.new, I certainly get an array whose #initialize >> has been called. >> >> But if I then do arr.class = Net::HTTP, then Net:HTTP#initialize >> does NOT get called (e.g., to set the host name or whatever). > > Ah...I see why I haven't connected with that issue now. I think of > this as a > "buyer beware" issue. If you change an existing object's class, be > sure you > change it to something that understands the object it is receiving. I > think > even #become would have these problems. Even using include could > introduce > methods to a class which didn't initialize instance data the way it > expects. The problem is that you are splicing code maintained by one person with code maintained by another. Who is responsible for this? You claim "buyer beware", that the user of #class= is responsible, but earlier in this thread you state [ruby-talk 104578]: > But, I think simply placing checks in certain appropriate places would > alleviate the problem. Sometime today I think I'll try putting type > checks > in the R_CAST macro and see how that works. If you really believed that this method was "buyer beware", why are you suggesting the Ruby internals be fixed? That implies that Ruby is responsible for failures beyond a simple exception, that Ruby should be altered to make it safe for #class= . You project two cases after a call to #class= : * the first use of a method on the new object immediately fails with an exception * the new class was defined properly, and does what you need. But there is a third case: * the first use of a method on the new object quietly fails, possibly in ways that cause permanent damage to your system. One can protect against this sort of problem safe is to turn #class= off by default, adding a hook on Object: def update_instance_for_changed_class( old_instance ) raise 'Unimplemented' end Note that to make the api safe, we had to turn it off for all but explicitly coded conversions. If we have to be so explicit to be safe, why not just have an explicit method to do the conversion? Note matz has already made this design decision. When converting types one uses explicit, clearly implemented methods like #to_s or #to_a to convert one type to another. Finally, I have not yet seen an example that could not be handled trivially in some other manner, using delegators, or re ordering code a bit: class WriteLogger < SimpleDelegator def write( args* ) $stderr.puts "write called" super() end end socket = WriteLogger.new( socket ) socket.write( ... ) -------------- irb(main):001:0> class MyArray < Array; def reverse; sort_by {rand}; end; end => nil irb(main):002:0> array = MyArray[ 1, 2, 3] => [1, 2, 3] irb(main):003:0> array.reverse => [2, 1, 3] irb(main):004:0> -------------- # this doesn't work, # just an example of a decent # way to handle a complicated # conversion class Net::HTTP def to_tempfile temp = Tempfile.new temp.write Net::HTTP.start( self.address, self.port ) { |h| h.get( self.path, self.query, self.headers ) } temp end end It is irresponsible to add a method to Object: * that is typically broken by default * that introduce risks that have to be bandaged by more cruft * that obfuscates the problem it attempts to solve * that attempts to solve problems which are better solved in other manners #class= is pretty much useless, unless you want to break something. And for breaking things, I generally find that #raise fits my needs. Cheers, Patrick