Is it considered Harmful?

A

Austin Ziegler

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.

I think that Matz is going to provide a much better facility for this
sort of thing with the :pre and :post method wrappers (AOP-style
programming). The only valuable form of object replacement, IMO, is
replacing a proxy object with a real object.

-austin
 
G

Gavin Sinclair

Austin said:
The only valuable form of object replacement, IMO, is
replacing a proxy object with a real object.

That sounds about right to me. Aren't there two separate issues here?
'become', to me, means "become another **object**", not "change your
class".

I don't even know what

x = X.new
x.class = Y

is supposed to mean. But I do understand the idea behind this:

x = X.new
y = Y.new
x.become y

Are the two ideas really one and the same in this thread?

Gavin
 
P

Patrick May

Gavin,

That sounds about right to me. Aren't there two separate issues here?
'become', to me, means "become another **object**", not "change your
class".

You're right, there was a bit of confusion. I just reviewed evil.rb,
#become swaps instances of objects, it doesn't fiddle with the object's
class. The two are really quite different. :)
I don't even know what

x = X.new
x.class = Y

is supposed to mean.

This is the part that I find absurd. A class isn't just a collection
of methods (as a Module is); it's also the lifecycle of initialize.
That's the reason I find #class= to be harmful [ruby-talk 104494]
But I do understand the idea behind this:

x = X.new
y = Y.new
x.become y

Are the two ideas really one and the same in this thread?

They shouldn't be. I certainly mixed them up. My apologies; to be
clear, my concern is with #class= and not #become.

Cheers,

Patrick
 
S

Sean O'Dell

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

The class that created the object should have already initialized it.
Initialization is part of object creation, usually.

Sean O'Dell
 
S

Sean O'Dell

That sounds about right to me. Aren't there two separate issues here?
'become', to me, means "become another **object**", not "change your
class".

I don't even know what

x = X.new
x.class = Y

is supposed to mean. But I do understand the idea behind this:

x = X.new
y = Y.new
x.become y

That's funny, because the first one makes immediate sense to me, but I'm not
sure what the second one does.

What happens when x becomes y? Methods are replaced?

Sean O'Dell
 
G

Gavin Sinclair

Sean said:
That's funny, because the first one makes immediate sense to me, but I'm
not sure what the second one does.

What happens when x becomes y? Methods are replaced?

No, the object x points to is discarded and x points to y instead.
Moreover, *every* pointer to x now becomes a pointer to y, making the
statement a little more potent than

x = y

The only description of #become that ever made sense to me was "like
String#replace or Array#replace, but more general". The only
rationalisation for this feature that ever made sense to me was, as usual,
provided by Jim Weirich, in this thread. That is, replacing a proxy with
a "real" object. I'm pretty sure he was talking about #become, not
#class=.

Cheers,
Gavin
 
T

ts

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

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

no comment


Guy Decoux
 
V

vruz

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.


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

This particular example you propose with files can be done with
Aspect Oriented Programming.
For that specific need there's this: http://aspectr.sourceforge.net/
by Avi Bryant and Robert Feldt.
It's still alpha, and has been so for about two years, so I'm not
sure if you want to use it in the real world.
Anyway it's a nice thing to toy with.

cheers,

--- vruz
 
P

Patrick May

Sean,

The class that created the object should have already initialized it.
Initialization is part of object creation, usually.

So, SomeClass is responsible for initializing an object not only for
its own purposes, but also for Tempfile's purposes?

How does SomeClass know that you'll want to change the object's class
to a Tempfile, and not a Net::HTTP?

Do you see how this absurdity is the logical outcome of your statement?

Cheers,

Patrick
 
S

Sean O'Dell

This particular example you propose with files can be done with
Aspect Oriented Programming.
For that specific need there's this: http://aspectr.sourceforge.net/
by Avi Bryant and Robert Feldt.
It's still alpha, and has been so for about two years, so I'm not
sure if you want to use it in the real world.
Anyway it's a nice thing to toy with.

You can actually do it similarly by simply replacing methods you want to
handle and removing the ones you don't. The only problem I see with that
approach is the case when a method somewhere finds a method you didn't
anticipate and calls it. I think AOP would be subject to that same issue.

Sean O'Dell
 
S

Sean O'Dell

No, the object x points to is discarded and x points to y instead.
Moreover, *every* pointer to x now becomes a pointer to y, making the
statement a little more potent than

x = y

The only description of #become that ever made sense to me was "like
String#replace or Array#replace, but more general". The only
rationalisation for this feature that ever made sense to me was, as usual,
provided by Jim Weirich, in this thread. That is, replacing a proxy with
a "real" object. I'm pretty sure he was talking about #become, not
#class=.

In that case, #become sounds like a better way to get pretty much the same
effect as class=. I just wonder, though, how on earth you find every
reference to an object, even in C extensions. I guess if the GC can walk the
stack and find Ruby objects, it could be walked for that purpose, too.

Sean O'Dell
 
S

Sean O'Dell

Sean,



So, SomeClass is responsible for initializing an object not only for
its own purposes, but also for Tempfile's purposes?

How does SomeClass know that you'll want to change the object's class
to a Tempfile, and not a Net::HTTP?

Do you see how this absurdity is the logical outcome of your statement?

I think you're not understanding what class= does.

All class= does is change an existing object's class. The object already
exists; it was already initialized and is ready for use. All class= does is
simply change the object's class. All the initialized data is still there
with the class; still initialized. The object doesn't have to be initialized
in any particular way. The original class doesn't have to do any extra work
at all; none. All that happens is, the existing object's class is changed.
A class is pretty much just a container of methods, so all you're really
doing is changing all of the object's instance methods. But the initialize
method doesn't matter because the object already exists and doesn't need to
be called again.

Sean O'Dell
 
M

Mikael Brockman

Sean O'Dell said:
I think you're not understanding what class= does.

All class= does is change an existing object's class. The object already
exists; it was already initialized and is ready for use. All class= does is
simply change the object's class. All the initialized data is still there
with the class; still initialized. The object doesn't have to be initialized
in any particular way. The original class doesn't have to do any extra work
at all; none. All that happens is, the existing object's class is changed.
A class is pretty much just a container of methods, so all you're really
doing is changing all of the object's instance methods. But the initialize
method doesn't matter because the object already exists and doesn't need to
be called again.

Sean O'Dell

I think what Patrick means is that all methods assume that their class's
initialization method has been called. Currently, it's guaranteed to be
called, since #new calls it. What you're proposing -- #class= --
removes that guarantee.

mikael
 
S

Sean O'Dell

I think what Patrick means is that all methods assume that their class's
initialization method has been called. Currently, it's guaranteed to be
called, since #new calls it. What you're proposing -- #class= --
removes that guarantee.

#class= can only be called on existing objects, so it can't prevent
initialization from happening (it already happened before you can even make
the call). The takeover class inherits an already-initialized object. I'm
not sure how else to say that. The object is already initialized, #class=
cannot prevent that. Both classes, the old and the new, work with an
already-initialized object.

Sean O'Dell
 
A

Austin Ziegler

#class= can only be called on existing objects, so it can't prevent
initialization from happening (it already happened before you can even make
the call). The takeover class inherits an already-initialized object. I'm
not sure how else to say that. The object is already initialized, #class=
cannot prevent that. Both classes, the old and the new, work with an
already-initialized object.

So ... with your modifications, I can safely do:

foo = Object.new
foo.class = Tempfile
foo.write "bar"

It should be utterly obvious to anyone smarter than George Bush that
this simply won't work. Tempfile must be properly initialized using
Tempfile#new, and simply saying that foo is now a Tempfile doesn't
actually initialize those things upon which Tempfiles methods rely.
Yes, foo is initialized, but it's not a properly initialized Tempfile
even though you've called it one.

Pathological case? You bet. But simply doing #class= doesn't do the
proper initialization. #become (or #swap, if you prefer) is far more
useful and practical, but only in cases of type refinement (and I'm
dubious on the value of that) or proxy replacement (and I'm
enthusiastic about the value of that). I really don't think that the
danger of either one of them is worth the limited value that it
represents given that smart programming can get around the problem in
both cases.

-austin
 
S

Sean O'Dell

So ... with your modifications, I can safely do:

foo = Object.new
foo.class = Tempfile
foo.write "bar"

Safely, yes. Usefully, no. Tempfile can't do much with just an Object
instance, and since its methods expect its own data and nothing else, it
really would just complain that it can't do anything useful.

However:

socket.class = SocketLogger

...would be a case where something useful DID happen. Assume you get socket
in a callback method of, say, an HTTP server framework. You can't figure out
why your client code isn't getting the data you think your callback method is
sending through the socket object. So, as a debugging trick, you swap out
the socket object with a SocketLogger, which still sends data through the
socket, but also logs everything sent by your method as well as the
framework, so you can see everything going out by just analyzing a logging
file. From that point on out, every method call made on the socket object
would be handled by the SocketLogger class; no calls could slip through to
the old Socket class and go unnoticed accidently.

Both #class= and #become, I think, would serve this purpose. I like #become
for its safety, but I boggle at how you would located every single reference
to an object both in Ruby and in C extensions. Also, there's the chance that
an object could #become to another class and still be assigned methods which
expect certain data to be initialized which is not, and you could still end
up with the same dangers as #class=.

Sean O'Dell
 
S

Sean O'Dell

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

S> So, I'm not sure what your point is. Are you annoyed that it worked?
^^^^^^^^^
no comment

I'm going to ask this one time, and please everyone forgive me for going
off-topic, but could you leave out the personal stuff when we're talking
about Ruby? You and Ziegler both have a habit of being very nasty with me.
Let's discuss Ruby without the personal nastiness, could we?

Sean O'Dell
 

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,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top