Is it considered Harmful?

J

Jean-Hugues ROBERT

Florian Gross said:
Jim said:
Florian Gross said:
[problems with proxies]
Comparisons are problematic because the first line of a comparison is
often "Are you the same class as me?". Proxies arent't the right class so
the comparisons fail.

It depends -- the answer to that question can be overloaded in most
cases. (Though Object.instance_method:)class).bind(obj).call will
always give you the real class and I think that some of the internal
methods actually do something like that.)
Actually, rather than a become or class= method, I would be interested in
a swap_identitys method. For example, to make a proxy object real ...
def make_real
obj = read_real_object_from_the_database
swap_identities(self, obj)
end
When identities are swapped, every reference in the system to the
first
object will magically become a reference to the second object, and
vice-versa.

This is how Squeak implements this AFAIK -- it just iterates through
all Object references and changes them.

I'm not sure, but this could be a bit slow, maybe. (And I'm not sure
if it would be a general solution for the problems with Proxy objects
-- it would work in the lazy database example, but there might be
other problematic use cases.)

How about changing the object to a redirection pointer, and also
updating references in the garbage collector's root set traversal?
You'd get some overhead at first, but after one garbage collection, all
references will be updated and the redirection object can be recycled.

mikael

OTOH you don't want to make a slow thing even slower and it
is my understanding that current Ruby's GC has enough room
for improvement that a serious re-implementation is in
progress.

I guess the change (removing the additional level for indirection)
could be done when the pointer is checked for redirection.

That stills mean an additional check on every use of a
reference. Which is a lot I guess.

Thoughts: Maybe "become" semantic can be implemented (as already
suggested) with some "proxy" solution. But a highly optimized/
specialized "become" oriented C implementation. As a result,
use of objects that "became" something else would be slightly
slower than use of "normal" objects, but "normal" objects would
keep their current speed. Nota: The "class" of the "become
proxied object" has to be the proper one, to avoid the issue
with proxies involved in comparisons that check classes.

That way, you pay for the feature only when you use it.

Yours,

JeanHuguesRobert
 
T

ts

S> Which is pretty solid, and now I can change the class of an object. A patch
S> against the Ruby CVS follows, for reference.

Try first to test your patch.


Guy Decoux
 
S

Sean O'Dell

S> Which is pretty solid, and now I can change the class of an object. A
patch S> against the Ruby CVS follows, for reference.

Try first to test your patch.

I did; it works fine.

Sean O'Dell
 
T

ts

S> I did; it works fine.

Just a few things :

* there is a french word for what you are doing : apprenti-sorcier

* I've never understood what problem you are trying to solve

* matz has said that he don't want it.



Guy Decoux
 
S

Sean O'Dell

S> I did; it works fine.

Just a few things :

* there is a french word for what you are doing : apprenti-sorcier

* I've never understood what problem you are trying to solve

* matz has said that he don't want it.

I did it, and it worked like I said it could. I didn't say Matz would accept
it or want it. In fact, I know from looking at the Ruby internals it would
be very hard to make totally stable, so I wouldn't want it accepted myself,
not until a lot of code was bullet-proofed internally.

So, I'm not sure what your point is. Are you annoyed that it worked?

Sean O'Dell
 
S

Stop SOD

So, I'm not sure what your point is. Are you
annoyed that it worked?

The question is, "Are you annoyed that matz didnt want
it?". Stop bsahing people around or you will find
yourself on the other end. Also, go read some CS books
while you are at it. We all know you are a teenager
with too much time and jealous of the people with
degrees. You are/have been playing suck up on the MLs.
Caio !



__________________________________
Do you Yahoo!?
Take Yahoo! Mail with you! Get it on your mobile phone.
http://mobile.yahoo.com/maildemo
 
M

Mark Sparshatt

an said:
The question is, "Are you annoyed that matz didnt want
it?". Stop bsahing people around or you will find
yourself on the other end. Also, go read some CS books
while you are at it. We all know you are a teenager
with too much time and jealous of the people with
degrees. You are/have been playing suck up on the MLs.
Caio !
Do you know one of the many things I like about Ruby?

The very friendly and helpful mailing list.

Please stop trying to ruin that.
 
P

Patrick May

I think that #become and #class= are _theoretically_ wrong. Such a
method makes handling object life cycle ugly.

Objects expect to be initialized first, they expect to have various
pieces of state setup in a predictable manner. If an object of one
class #become's another class, then the new class's methods can't count
on having the proper state. Core dumps is what you get when this
happens in c, but you'll have similar problems with 100% *.rb code.
Edge cases not being handled correctly, missing instance variables, etc.

To get around these errors, you end up needing an extra initialize
function. This extra initialize always takes one argument, the object
that is becoming the class. Tracking this extra lifecycle per class
sounds like a recipe for bugs.

To safely support this code:

a = SomeClass.new
a.become( AnotherClass )

I end up needing this code in AnotherClass:

def update_instance_for_changed_class( old_instance )
case old_instance.class
when SomeClass
self.initialize( old_instance.attribute1,
old_instance.attribute2 )
when ....
....
end
end

Other than being uglier, I don't see how #become is different from:

a = SomeClass.new
a = AnotherClass.new( a.attribute1, a.attribute2 )

This example gets the mapping of SomeClass to AnotherClass out of
AnotherClass and into the code that wants to map the two together. And
my variable has effectively "become" a new class.

Is the problem that you want to add methods? Then use Modules.

Is the problem that you want to update all references to the new a?
For one, #replace_self raises different issues than #become. Still, I
have a hunch that for most uses of #replace_self there is a way to use
of accessor methods instead.

So, to sum up my ivory tower critique of #become and #class=

* #become creates a duplicate lifecycle for objects, resulting in
lots of complication
* It doesn't really add that much functionality

Cheers,

Patrick

p.s. Thanks for raising an interesting issue! I enjoyed following the
conversation and thinking about the implications of #become / #class=
 
S

Sean O'Dell

Is the problem that you want to add methods? Then use Modules.

This is a good question. In the past, I've wished I could change objects into
a different state, sort of like taint or freeze, but with a little more
customization. For example, taking a File object and replacing all of its
write methods to custom ones that log everything written. It can be done by
adding methods (and removing methods ones you don't want to be available
anymore), but I thought a really super simple way to handle the situation is
to simply replace the entire class of the object, so you control 100% what
methods are available. That way, if the File object got passed to some other
method out of your control that called a method you forgot to replace or
remove, the method wouldn't even be there.

Of course, like I said, the Ruby internals make difficult to make a stable
feature, so the point is probably moot. But what I found interesting was how
easy it was to change an object's class in C, and to provide a fair amount of
protective code by simply adding a Check_Type call to the R* macros.
* #become creates a duplicate lifecycle for objects, resulting in
lots of complication
* It doesn't really add that much functionality

I think if Ruby could handle it with stability, it would simply be another
possibility. Some people would find it hard to use, others would find it
very powerful. I personally am not afraid of such things; I find myself
thinking of endless uses for it.

Sean O'Dell
 
P

Patrick May

This is a good question. In the past, I've wished I could change
objects into
a different state, sort of like taint or freeze, but with a little more
customization. For example, taking a File object and replacing all of
its
write methods to custom ones that log everything written. It can be
done by
adding methods (and removing methods ones you don't want to be
available
anymore), but I thought a really super simple way to handle the
situation is
to simply replace the entire class of the object, so you control 100%
what
methods are available. That way, if the File object got passed to
some other
method out of your control that called a method you forgot to replace
or
remove, the method wouldn't even be there.

Delegation sounds like the solution here, either hand coded or with
SimpleDelegator.
Of course, like I said, the Ruby internals make difficult to make a
stable
feature, so the point is probably moot. But what I found interesting
was how
easy it was to change an object's class in C, and to provide a fair
amount of
protective code by simply adding a Check_Type call to the R* macros.

Protecting Ruby from a segfault is not going to insure that all
changling objects work properly.

How can any class, not just the Ruby internals, make an intelligent
decision about how to initialize an instance from a random operand?

The problem with #become / #class= is that it implies one can easily
change the class of any object into any other object. Changing the
class is easy, but for the new object to work the new class has to map
the state and semantics of the old class into the state and semantics
of the new class. The #update_instance_for_changed_class tries to
solve this problem, but this is poor encapsulation. For #become to be
safe and easy, every class has to know details of the internal state of
every other class.

Again, why should every class be saddled with the responsibility of
knowing the internal semantics of literally *every* other class in the
system? Those #update_instance_for_changed_class methods keep getting
larger and larger....

#become / #class= is an interesting thought experiment, but it becomes
absurd when I imagine what it would take to make it work properly.
These methods shift a responsibility that is easiest to handle outside
a class into somewhere it doesn't belong. That is ugly, difficult,
and not Ruby-like IMHO.

Cheers,

Patrick
 
S

Sean O'Dell

Protecting Ruby from a segfault is not going to insure that all
changling objects work properly.

How can any class, not just the Ruby internals, make an intelligent
decision about how to initialize an instance from a random operand?

Well, I think the rule here is: buyer beware. The only real use for class=
would be when you are replacing one class with another that KNOWS and
UNDERSTANDS that it's a replacement class, and behaves appropriately.
solve this problem, but this is poor encapsulation. For #become to be
safe and easy, every class has to know details of the internal state of
every other class.

A class only has to check its own internal data, not know and understand
everyone else's. If it can't find its own internal data, or the internal
data is clearly of a type belonging to some other class it doesn't know
about, it should degrade gracefully by throwing some kind of "bad internal
data" exception.
Again, why should every class be saddled with the responsibility of
knowing the internal semantics of literally *every* other class in the
system? Those #update_instance_for_changed_class methods keep getting
larger and larger....

It shouldn't; it should only know about its own data, and ignore everyone
else's.
#become / #class= is an interesting thought experiment, but it becomes
absurd when I imagine what it would take to make it work properly.
These methods shift a responsibility that is easiest to handle outside
a class into somewhere it doesn't belong. That is ugly, difficult,
and not Ruby-like IMHO.

The internal Ruby code, in a lot of places, assumes the type of object that
self points to. This kind of assumption is the one that brings out the
crashes. This is the real hurdle. If every internal checked its own self
type before trying to use it, and degraded gracefully when it doesn't have
the right internal data, this feature would be perfectly safe.

Writing classes that understand the internal data of other classes is up to
the developers themselves. For the most part, you couldn't set the class of
a File object to a Hash and expect much to happen except for "bad internal
data" errors. However, someone could develop a class that understands the
internal data of the File object and it would be perfectly safe to replace
File object classes with the new class.

Sean O'Dell
 
P

Patrick May

Hi Sean,

Well, I think the rule here is: buyer beware. The only real use for
class=
would be when you are replacing one class with another that KNOWS and
UNDERSTANDS that it's a replacement class, and behaves appropriately.

you don't need #class= for this:

a = SomeClass.new
a = SimpleDelegator.new(a)

Cheers,

Patrick
 
P

Patrick May

Sean,

The internal Ruby code, in a lot of places, assumes the type of object
that
self points to. This kind of assumption is the one that brings out the
crashes. This is the real hurdle. If every internal checked its own
self
type before trying to use it, and degraded gracefully when it doesn't
have
the right internal data, this feature would be perfectly safe.

Requiring a check that self is consistent before an object does
anything useful doesn't sound like fun.

class SomeClass
def a_method
raise "I'm not myself" unless (self.class.kind_of? SomeClass)
...
end

def another_method
raise "I'm not myself" unless (self.class.kind_of? SomeClass)
...
end

... ad nauseum...
end

I think there are other languages that require you to be a strong
typist, if that is what you are interested in.... :)

Cheers,

Patrick
 
M

Mauricio Fernández

To safely support this code:
a = SomeClass.new
a.become( AnotherClass ) [...]
Is the problem that you want to update all references to the new a?
For one, #replace_self raises different issues than #become. Still, I
have a hunch that for most uses of #replace_self there is a way to use
of accessor methods instead.

The Object#become in evil.rb does what you call #replace_self (AFAIK
become is the standard name for that).

evil.rb is meant to be used by those who want to explore the possibilities
offered by those methods. We neither recommend nor discourage its use. You
have to keep in mind that it is potentially unsafe, although we have
tried to make it safe with lots of checks and restrictions. However, maybe
somebody will find a reasonable use for them (especially Object#become),
and those who want to play with this stuff can use evil.rb instead of
wasting matz' time.

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

...[Linux's] capacity to talk via any medium except smoke signals.
-- Dr. Greg Wettstein, Roger Maris Cancer Center
 
S

Sean O'Dell

Sean,



Requiring a check that self is consistent before an object does
anything useful doesn't sound like fun.

class SomeClass
def a_method
raise "I'm not myself" unless (self.class.kind_of? SomeClass)
...
end

def another_method
raise "I'm not myself" unless (self.class.kind_of? SomeClass)
...
end

... ad nauseum...
end

I think there are other languages that require you to be a strong
typist, if that is what you are interested in.... :)

Well, in C extensions you have typing, period; you can't get around that. In
C you quite often have no choice but to check your type. In fact, the vast
majority of security hacks arise out of C programmers not checking variables
for type and/or size. It's results in buffer overflow-style hacks that not
only result in software de-stabilization, attackers can sometimes gain
permissions through them.

Besides, the Ruby internals have a set of static Ruby types that, whether you
like strong typing or not, are very static, very typed and will explode as
well as anything else if you use them without checking what type you are
using and whether it's been initialized properly or not. File, Hash, Array,
String, FixNum and some others are all static, internal Ruby types that are
very static and you simply can't treat them in C like you do in Ruby code.

As far as Ruby classes, they have instance @variables, and it's far less of an
issue. If you try to use an instance @variable that isn't there, or does
something you don't expect, that's simply known as "duck typing." Just use
the variable and hope for the best.

Sean O'Dell
 
P

Patrick May

Well, in C extensions you have typing, period; you can't get around
that. In
C you quite often have no choice but to check your type. In fact, the
vast
majority of security hacks arise out of C programmers not checking
variables
for type and/or size. It's results in buffer overflow-style hacks
that not
only result in software de-stabilization, attackers can sometimes gain
permissions through them.

Classes defined in safe old 'script.rb' files will have to deal with
the same lifecycle issues as classes defined in C. You might not see
core dumps, but same issue remains. Any class which depends on its
initialize method will have guard against #become.

For example -- the very useful class Tempfile is defined in
tempfile.rb. It depends on these variables:

@tmpname: the name of the tempfile
@tmpfile: the File object of the tempfile
@@cleanlist: the list of files to be cleaned on exit, @tmpname gets
appended to it.

If these variables are out of sync, then Tempfile is broken. Note the
class variable @@cleanlist. A Tempfile might #become something else
that changes @tmpname and @tmpfile, but that new class can't change
@@cleanlist.

This is what I mean when I refer to lifecycle issues. It has nothing
to do with C; every class in Ruby will have to guard against this sort
of problem.

And I don't think that sounds like fun.

Cheers,

Patrick
 
S

Sean O'Dell

Classes defined in safe old 'script.rb' files will have to deal with
the same lifecycle issues as classes defined in C. You might not see
core dumps, but same issue remains. Any class which depends on its
initialize method will have guard against #become.

For example -- the very useful class Tempfile is defined in
tempfile.rb. It depends on these variables:

@tmpname: the name of the tempfile
@tmpfile: the File object of the tempfile
@@cleanlist: the list of files to be cleaned on exit, @tmpname gets
appended to it.

If these variables are out of sync, then Tempfile is broken. Note the
class variable @@cleanlist. A Tempfile might #become something else
that changes @tmpname and @tmpfile, but that new class can't change
@@cleanlist.

This is what I mean when I refer to lifecycle issues. It has nothing
to do with C; every class in Ruby will have to guard against this sort
of problem.

Those variables will still exist; all that changes is the class that handles
them. The original class still does all the initialization work. #class=
changes the class of an existing object, so the old class has no more
responsibility once it has been replaced. The class replacing it takes over
all functionality.

Sean O'Dell
 
P

Patrick May

Those variables will still exist; all that changes is the class that
handles
them. The original class still does all the initialization work.
#class=
changes the class of an existing object, so the old class has no more
responsibility once it has been replaced. The class replacing it
takes over
all functionality.

If an object of SomeClass becomes a Tempfile (or any other class with
significant internal state), which class is responsible for the
initialization work?

~ Patrick
 
P

Patrick May

Mauricio,

The Object#become in evil.rb does what you call #replace_self (AFAIK
become is the standard name for that).

I think that

* Map object of class A to an object of class B

and

* replace all pointers of object C with pointers to object D

are stesp that would fare better if implemented separately. My case is
that the former cannot be performed in a generalized fashion, though
one can pretend. I think it may be possible to perform the latter in a
generalized manner.

I like the name 'evil.rb', btw :)

Cheers,

Patrick
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top