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