Is it considered Harmful?

Discussion in 'Ruby' started by rolo, Jun 25, 2004.

  1. Could you try

    a = Thread.new{}; a.class = Proc; a.call

    ?

    That used to crash in evil.rb some time ago.

    --
    Running Debian GNU/Linux Sid (unstable)
    batsman dot geo at yahoo dot com

    Whoa...I did a 'zcat /vmlinuz > /dev/audio' and I think I heard God...
    -- mikecd on #Linux
     
    Mauricio Fernández, Jun 28, 2004
    1. Advertisements

  2. rolo

    Sean O'Dell Guest

    Nope, no sarcasm from me there. I wish I'd known that about Guy's expertise
    early on; I would have given his opinion more weight. I try not to make
    assumptions either way, but I rarely accept someone on their word when they
    make generalizations. If a person makes a claim and doesn't provide any
    background, I generally dismiss the claim. I learned for myself why #class=
    was potentially dangerous. Even Matz didn't offer any clues as to why it
    would be dangerous, but at least I am aware of his level of knowledge to
    simply take him at his word on the matter. I didn't know Guy had any inkling
    about Ruby's internals, and he didn't offer much specific insight. Even the
    threads I was directed to read made only vague reference to potential
    problems. I learned what some of the specific problems are, but only from
    implementing the feature myself.

    Sean O'Dell
     
    Sean O'Dell, Jun 28, 2004
    1. Advertisements

  3. rolo

    Sean O'Dell Guest

    :258: warning: multiple values for a block parameter (0 for 1)
    from testclass.rb:1
    :258: [BUG] bug in variable assignment
    ruby 1.9.0 (2004-06-25) [i686-linux]

    Aborted



    Sean O'Dell
     
    Sean O'Dell, Jun 28, 2004
  4. il Tue, 29 Jun 2004 06:17:56 +0900, Hal Fulton
    I won't bet on him being human ;)
     
    gabriele renzi, Jun 28, 2004
  5. Perhaps this might be a "better" way, for some value of "better":

    module SocketLogger
    ...
    end

    socket.extend(SocketLogger)

    I certainly don't see how it's worse. I bet there's a way you could
    change any object at runtime to log every call to every method, too.

    Gavin
     
    Gavin Sinclair, Jun 28, 2004
  6. No it wouldn't. Objects are supposed to represent data and behaviour.
    #become keeps both intact. #class= changes only the behaviour. If
    something does't work after #become, it's not because the use of
    #become introduced an inconsistency. The same cannot be said of
    #class=. That's not necessarily a point against it...
    ...as this demonstrates. Ruby contains lots of features that make it
    easy to write broken programs. #class= should be considered in that
    light. I don't see it as being very useful (stateless behaviour can
    be elegantly captured by modules), but I'm sure it could be.

    Gavin
     
    Gavin Sinclair, Jun 29, 2004
  7. rolo

    Sean O'Dell Guest

    I realized my error regarding #become after reading another post.
    I think both #class= and #become have a lot of the same potential pitfalls,
    but I agree that #become encourages better data encapsulation. Assigning the
    data of an object to another class arbitrarily (such as is possible with
    #class=), in OOP terms, is a bit out there.

    I should try and remember how well Ruby implements OOP and how such
    suggestions would really rub people the wrong way who want to preserve that.
    I was only concerned with keeping Ruby from crashing, and adding C code to
    protect it more, and I really didn't consider how it would affect Ruby's
    OO-ness (I didn't say paradigm).

    Sean O'Dell
     
    Sean O'Dell, Jun 29, 2004
  8. rolo

    Patrick May Guest

    Sean,

    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]:
    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
     
    Patrick May, Jun 29, 2004
  9. rolo

    Sean O'Dell Guest

    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.
    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.
    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.
    Well, this is certainly the easiest route; just avoid the whole thing
    completely and let to_ be the conversion mechanism.
    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.
    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
     
    Sean O'Dell, Jun 29, 2004
  10. Gennady a écrit :
    *I* would ! Try to eat a piece of gold !-)
     
    bruno modulix, Jul 5, 2004
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.