On Monday 28 June 2004 17:54, Patrick May wrote: > > 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= . There are two problems with #class= that I was talking about, not just one. The first, and the one that causes the most turbulent problems, is that a lot of C code internally considers self VALUEs to be of various static types, and there are few checks to ensure proper type or status before self VALUEs are used. That causes segmentation faults and potentially other various nastiness. This problem can be solved with protective code, but only someone with a lot of knowledge of Ruby's internals would know where every dangerous code point is; that's not something I could not do by myself. The other problem is when one class simply doesn't know how to handle the data contained in an object which was created by another class. That's the "buyer beware" issue I was talking about. Assuming #class= were to be implemented the way we've been discussing it, what methods do when you assign a new class to an existing object is something the developer decides; they decide which class they assign to which object, and what the end result will be. > 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. This is already possible anyway. Ruby has no type checking, and it's very possible to make method calls on objects that you expect to do one thing, but which do another. From what I understand, this hasn't been much of a problem; I think people consider this one possible result of duck-typing, and it just hasn't manifested itself as destructively as some have feared (myself included). The immediate problem is the crashing in C code. Beyond that, its general break from OO causes OO dysfunction which, like duck typing, can cause unexpected behaviors. Its not so much the unexpected behavior that's the problem (you can get that easily in lots of ways already), its that it simply isn't OO. > 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 I also considered something like this as just one of a lot of things that could be done to make #class= safe, but all this work starts to make #class= look like just a very bothersome, non-OO version of #become. I'm really more a fan of #become over #class= now. > 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. Well, this is certainly the easiest route; just avoid the whole thing completely and let to_ be the conversion mechanism. > 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( ... ) The scope of this is limited though. If you need the logger to replace, say, a socket object that was created by a library, the logger would only function in the scope of your method(s); once the logger went out of scope, the rest of the library would go back to using the socket object it created. > 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. Ruby already lets you break things wonderfully in a colorful variety of ways. #class= does have issues, but the problems that arise from inserting methods into an object which don't understand the object very well is really something you can do easily in a lot of other ways. #class=' biggest trouble is that it causes crashed with internal C code, and that it breaks from OO tradition. Sean O'Dell