Self and Ruby Comparisons

Z

Zach Dennis

Robert said:
I missed the term "role" in your explanation. Cause that's the way I had
described this: the person changes her roles over time. Changing the
class is certainly an option here although in this example you would
likely want to restrict classes to sub classes of Person.

But what do you do if the Person needs to play several roles at a time?
Change the class for each method invocation? That's likely going to be
inefficient - and considering multiple threads it restricts concurrency
dramatically.

In Ruby the ideal way IMHO would be to have the reverse method of #extend
so if an instance takes on a role it uses a special mixin module's methods
and un-mixes this module when it leaves the role.

I am thinking along the same lines as Robert here T. I don't see why you
would want to change the class of an object to become something else, it
seems if that comes up their was a flaw in the design of the Person class.

Thinking intuitively on this if you had a person who was a Construction
worker and became a fireman, would you really remove the persons'
ability to be a construction worker to be a fireman? I'd say no. The
person will still know how to build things, you're just extending what
they know how to do or their *role* as Robert put it. There's no need to
add extra work and start swapping baseclasses.

Zach
 
Z

Zach Dennis

Trans said:
robert,

Quite right, I'm not sure Ruby is particularly suited for role
swapping. (Prototype-based OOP handles it gracefully) And I don't know
how inefficent #become would be. It would be nice to think its just a
matter of changing one pointer, but I know that's not the case.

Your role delegator is nice. At the very least it's much easier to be
more than one thing at a time. (There's no problem with roles
interfereing with each at the method name level is there?) Unfortuately
I suspect that it may be nearly as inefficient as repeated using
#become (but that's just a guess).

Thanks for the illustration, I think I'll store this for potential
future use/exploration.

BTW, does #unrole actually work?

From looking at the Core API it doesn't appear there is an easy way to
remove included Modules.

I'd suggest to wrap *behaviors* into a Role object, and add that role
object to a Person.

class Person
@roles = []
def add_role( r ); @roles << role
end

class Role
@behaviors = []
def add_behavior( b ); @behaviors << b; end
def each_behavior( &b ); @behaviors.each{ |b| yield b.do }; end
end

class Behavior
end

class StopFireBehavior < Behavior
end

class FiremanRole < Role
def initialze
add_behavior( StopFireBehavior.new )
end
end

joe = Person.new
fireman_role = Role.new( FiremanBehavior.new )
joe.add_role( FiremanRole.new )


This is just thinking outloud, not testing... so a person could have a
role, each role is defined by a set of behaviors...?

Zach
 
T

Trans

Ignoring request to ignore ;-) [ed- deja'vu flgr]

While it true an object may need to "multitask" in multiple roles,
there is alsow merit (as any good Buddhist knows) in focusingin on
what's being done at the moment. In other words, I don't see anything
inheriently wrong with swapping classes if that is the only the role at
hand. And it seems more intuitive to me.

Although I think this is more suitable to PbOOPs than it is to Ruby I
still don;t see anything wrong with allowing it via a #become method if
it is reasonably possible to implement.

T.
 
F

Florian Gross

Mathieu said:
Actually, Perl has it better, because you can change the class of an
object, and you can change the list of superclasses of any class.

Note that this can be mostly done with EvilRuby. You can change the
direct superclass via Class#superclass= and the class via Object#class=.
You can also insert new Classes into the inheritance chain with
Class#inherit (which works by doing include(klass.as_module)). Note that
there are still some limitations, sanity checks are a big one and
there's still no way of removing items from the inheritance chain.
 
F

Florian Gross

Trans said:
Quite right, I'm not sure Ruby is particularly suited for role
swapping. (Prototype-based OOP handles it gracefully) And I don't know
how inefficent #become would be. It would be nice to think its just a
matter of changing one pointer, but I know that's not the case.

It's still not too much work. Ignoring the checks that need to be done
the basic task works like this: Create a copy of the object and swap the
five fields of their internal structs -- after that invalidate the
method cache and optionally migrate exivars and perhaps also the
finalizer. I think you can safely assume that this overhead is constant.
 
R

Robert Klemme

Trans said:
robert,

Quite right, I'm not sure Ruby is particularly suited for role
swapping. (Prototype-based OOP handles it gracefully) And I don't know
how inefficent #become would be. It would be nice to think its just a
matter of changing one pointer, but I know that's not the case.

Your role delegator is nice. At the very least it's much easier to be
more than one thing at a time. (There's no problem with roles
interfereing with each at the method name level is there?)

No, as each role is handled by it's own delegator instance. The downside
is that roles cannot interact directly but need to go via role:

module Role1
def work() puts "r1: #{name}" end
end

module Role2
def work() puts "r2: #{name}"; role(Role1).work() end
end

class Person
attr_accessor :name
end
r1: Gavagai
=> nilr2: Gavagai
r1: Gavagai
=> nil
Unfortuately
I suspect that it may be nearly as inefficient as repeated using
#become (but that's just a guess).

Not necessarily as long as you don't continuously use unrole() - because
delegator instances are stored in a hash and thus are reused. So a role
can have it's own state - separated from the main instance's state which
is a good thing when it comes to prevention of name clashes. The only
other inefficiency stems from delegation (in the example above method
name() is delegated).

The more I think about using delegation for this the more I like it
because it has some nice properties: no problems with name clashes in
different modules, every role has it's own state that is dropped together
with the role etc. I'll attach a slightly reworked version.
Thanks for the illustration, I think I'll store this for potential
future use/exploration.

You're welcome.
BTW, does #unrole actually work?

Yes, but as soon as you do role() again, the role is created again. You
can see only via role?() whether a role is adopted or not. Of course one
could change the pattern to have a separate add_role() then you will get
an error if you use role() but the instance does not play the role - might
be better in terms of robustness.

Kind regards

robert
 
C

Csaba Henk

Even so, like I said, your approach does have merits, and is certainly
a good way to give prototype-based OOP some play in Ruby. If your are
interested I would enjoy developing a dedicated library together. We
can work on it over at suby-ruby (keep in mind I can't devote too muh
time on it at the moment, but I willl certainly be happy to help/get
started.)

I neither have much time, but we could try.

But before writing a word of ruby, a plan should be made and goals
should be made clear.

Three different things to achieve:

1) Extend ruby's OO kit with prototype-like techniques.
2) Create a class Prototype within which you can play full
prototype-based OO.
3) If 2) is done, create "bridges" between conventional ruby world and
prototype-based (eg., extend Object with a method which creates a Prototype
proxy)

Also it would be useful to get some insight where this stuff could be
used. Do you have any idea?

(Personal opinion:

prototype-based oo [poo] is most useful where object initialization
doesn't require some special resource. (Poo advocates insist on the
intuitiveness of poo, but for me its quite contraintuitive to clone a
File object; or, if there is a special constructor for the given type
because of this, it's also contraintuitive that any instance of that
type can give rise to a new copy where the only thing shared with the
parent and the children is the type. If there is a special object
bearing the constructor method that's so close to being a class that I
don't see the point in poo for this type.) Parsers might be a good
example. There the class is really such an abstraction which has no
use.)

You should give me a sketch what kind of object model do you think of
and what capabilities should the citizens of poo country have.

As a recent post pointed out, there is no module method like "uninclude"
(I always tought there is one). That's pretty sad, it weakens ruby's oo.
Is there a reason for being so, or just there was no reason to make one?

Csaba
 
T

ts

C> Is there a reason for being so, or just there was no reason to make one?

module A
end

module B
include A
end

class C
include A, B
end

now `uninclude B' (or `uninclude A') remove the module A or not ?

ruby has lost the information that A was provided by A and B

There is also : what do you do is someone write ?

class C
uninclude Kernel
end



Guy Decoux
 
M

Mathieu Bouchard

And if a copy is sufficient you can do this even in pure Ruby:

No, a copy is _not_ sufficient.
Do you have a link or explanation that demonstrates the merits of the
capability to change an instance's class at runtime? Currently I don't
have a clue in which situations I would want this. Thx!

Just like almost any other programming concept invented to improve
structured programming, it's about replacing if/case expressions by tables
functionpointers such that it creates a "separation of concerns". (i mean
all the various forms of polymorphisms)

Now, for that feature in particular, think of a File which may be open or
closed, and you have to write at the start of each damn method:

if not @file then raise "file not open" end

well, this could be solved by changing the set of all methods of that
class.

In general an object that has a few major cases of behaviour like that,
can benefit from switching methodtables.

In Ruby, my way to solve this problem is instead to use one object
representing a File that may be open or not, and one object that
represents a File guaranteed to be open, and then use message-forwarding
using Object#method_missing.

_____________________________________________________________________
Mathieu Bouchard -=- Montréal QC Canada -=- http://artengine.ca/matju
 
T

Trans

Csaba,

As a recent post pointed out, there is no module method like
uninclude" (I always tought there is one). That's pretty sad, it
weakens ruby's oo. Is there a reason for being so, or just there as no
reason to make one?
[quote.]

I don't think it is impossible, but I think there are subtle reasons
why it is problematic. But I don't think it shoud be too much of a
problem, I think roberts delegation mechanism will suffice for our
needs.

1) Extend ruby's OO kit with prototype-like techniques.
2) Create a class Prototype within which you can play full
prototype-based OO.
3) If 2) is done, create "bridges" between conventional ruby world and
prototype-based (eg., extend Object with a method which creates a
Prototype proxy)
[quote.]

Sounds good.

Also it would be useful to get some insight where this stuff could be
used. Do you have any idea?
[quote.]

It should prove useful for all problems as is a general purpose
programming paridigm, not a specific use case tool. The key to
eveluating out success will be to see how ell we can write a general
purpse app using only POOP (okay be have to get another acryonym! ;-)
and no COOP.

You should give me a sketch what kind of object model do you think of
and what capabilities should the citizens of poo country have.
[quote.]

Okay, hard stuff....

For starters we will need to create a core prototype for every core
class. That's simple enough for most things, but we do need to figure
out how to handle things like the File class, and actually IO in
general --I think we have to modify how they work slightly as
prototypes.

As I suggested above, I'm not sure we can use module inclusion as the
bases of "trait" inheritence, becuase of the uninclude problem. (Or
maybe we can, we'll have to give it some more thought.) But delegation
might do.

I have more details to add, but perhaps we should continue on suby-ruby
so as not to clutter ruby-talk with the details? You can sign up here:

https://lists.berlios.de/mailman/listinfo/suby-ruby

Work for you?

Oh and don't worry we can just take our good old time about it. There's
no rush, yes? :)

T.
 
T

Trans

Why the heck does Google Groups make such a mees of that!?

This is a test, please diregard the noise:

As I suggested above, I'm not sure we can use module inclusion as the
bases of "trait" inheritence, becuase of the uninclude problem. (Or
maybe we can, we'll have to give it some more thought.) But delegation
might do.
The above is quote. Does Groups automatically know this somehow?
 
T

Trans

Believie it or not, it does! And it's pushing down the last character
for some unknown "buggy" reason :-( Sigh.

Well, FYI, I guess.
 
R

Robert Klemme

Mathieu Bouchard said:
No, a copy is _not_ sufficient.


Just like almost any other programming concept invented to improve
structured programming, it's about replacing if/case expressions by tables
functionpointers such that it creates a "separation of concerns". (i mean
all the various forms of polymorphisms)

Now, for that feature in particular, think of a File which may be open or
closed, and you have to write at the start of each damn method:

if not @file then raise "file not open" end

well, this could be solved by changing the set of all methods of that
class.

In general an object that has a few major cases of behaviour like that,
can benefit from switching methodtables.

So basically you remove the wrapping instance from strategy pattern.
There will be different opinions about whether the distinction is more
approrpiately modeled by an object that changes its class from ClosedFile
to OpenFile or by an object with class File which changes its state from
"open" to "closed". I for my part prefer the second variant but I can see
the benefit of the other approach.
In Ruby, my way to solve this problem is instead to use one object
representing a File that may be open or not, and one object that
represents a File guaranteed to be open, and then use message-forwarding
using Object#method_missing.

Some more reading on the patterns discussed:

http://c2.com/cgi/wiki?StatePattern
http://c2.com/cgi/wiki?StrategyPattern

Kind regards

robert
 
C

Csaba Henk

C> Is there a reason for being so, or just there was no reason to make one?

module A
end

module B
include A
end

class C
include A, B
end

now `uninclude B' (or `uninclude A') remove the module A or not ?

Well, to fix a simple policy with which you can't go wrong is not hard.

So, for example, you could only uninclude a module if other included
modules don't depend on that. That is, in the above situation trying to
uninclude A would raise an error.
ruby has lost the information that A was provided by A and B

I see. In this case, you are on the safe side, if you just drop A.
It's a very simple, unambigouous action. Then let the user do the
bookeeping, if it's necessary in some situation.
There is also : what do you do is someone write ?

class C
uninclude Kernel
end

Is there any danger to let this? You can undef all methods one by one
now as well (leave "undef_method" for last :) ). If its problematic,
raise an error.

Csaba
 
Z

Zach Dennis

Csaba said:
Is there any danger to let this? You can undef all methods one by one
now as well (leave "undef_method" for last :) ). If its problematic,
raise an error.

This is pulled from the documentation for undef_method:

class Parent
def hello
puts "In parent"
end
end
class Child < Parent
def hello
puts "In child"
end
end

c = Child.new
c.hello

class Child
remove_method :hello # remove from child, still in parent
end
c.hello

class Child
undef_method :hello # prevent any calls to 'hello'
end
c.hello


How do you know if the user truly wants to block all calls to "hello",
or if they are just trying to remove the "hello" method on the Child
class? Perhaps it still should go up to the next "hello" method in the
ancestor chain...but undef_method doesn't work like that.

Zach
 
Z

Zach Dennis

Bah ignore this! I had thought I tested this, and that was the behavior,
but my test was flawed....

undef_method does work the way that Csaba implies.. my bad.

Zach
 
T

ts

C> Is there any danger to let this? You can undef all methods one by one

You have removed Kernel to *all* classes

C> now as well (leave "undef_method" for last :) ).

this is not the same.


Guy Decoux
 
C

Csaba Henk

C> Is there any danger to let this? You can undef all methods one by one

You have removed Kernel to *all* classes

I just realise that according to the scheme I proposed, this operation
doesn't make sense.

I told you may remove something if nothing else depends on it among
those who are mixed in. Thus you couldn't uninclude your superclasses'
mixins. (And noone spoke about the ability of discarding superclasses.)

That every class is a subclass of Object would shield you from
unincluding Kernel.

Csaba
 

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,997
Messages
2,570,239
Members
46,828
Latest member
LauraCastr

Latest Threads

Top