Is it considered Harmful?

M

Mauricio Fernández

Show me code that crashes my #class= implementation; I already assume
it can be done, now just show me how.

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
 
S

Sean O'Dell

Sean said:
Sean O'Dell wrote:
Oddly, Sean, when you are saying, "it worked like I said it could",
I am hearing Guy saying, "yes - but it doesn't work like I said it
wouldn't."

[what ts did.]

Ah, so it was insulting when I didn't accept his word outright. I see.
Well, I meant no harm; I didn't know Guy knew so much about the
internals he could make a quick call like that.

But if you really can't agree with me here, then I guess it is okay. In
that case I just feel a bit sorry that a common denominator could not be
found; that would make all the people who didn't even try to find one
appear in a better light than they deserve.

What are we disagreeing on? I understand that Guy knows enough about
Ruby that he can quickly determine the likelihood that #class= would ever
be safe enough for release. I'm not sure what part of what I said you
feel puts me in disagreement with you.

He thought you were being sarcastic in the paragraph starting "Ah..."
I had to read it twice, but decided you were not.

Guy (ts) is not superhuman, of course, but I would speculate that he
knows more about Ruby internals than anyone else outside Japan (and
quite a few or nearly all in Japan).

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
 
S

Sean O'Dell

Could you try

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

: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
 
G

gabriele renzi

il Tue, 29 Jun 2004 06:17:56 +0900, Hal Fulton
Guy (ts) is not superhuman, of course, but I would speculate that he
knows more about Ruby internals than anyone else outside Japan (and
quite a few or nearly all in Japan).

I won't bet on him being human ;)
 
G

Gavin Sinclair

socket.class = SocketLogger
...would be a case where something useful DID happen. [i.e. logging
all socket calls ...]

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
 
G

Gavin Sinclair

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.

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...
Even using include could introduce methods to a class which didn't
initialize instance data the way it expects.

...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
 
S

Sean O'Dell

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...

I realized my error regarding #become after reading another post.
...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.

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
 
P

Patrick May

Sean,

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
 
S

Sean O'Dell

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
 
B

bruno modulix

Gennady a écrit :
It would be nice to have in real life too. Just imagine: you hold an
apple in your hand and suddenly it turns into a piece of gold. I would
not mind ;-)
*I* would ! Try to eat a piece of gold !-)
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top