Self and Ruby Comparisons

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

···

On 2005-01-25, Trans <transfire@gmail.com> wrote:

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

"Mathieu Bouchard" <matju@sympatico.ca> schrieb im Newsbeitrag
news:Pine.LNX.4.21.0501261052280.16754-100000@mondrian.artengine.ca...

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

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

···

On Tue, 25 Jan 2005, Robert Klemme wrote:

"Robert Klemme" <bob.news@gmx.net> schrieb im Newsbeitrag
news:35nhdhF4pbf8dU1@individual.net...

"Trans" <transfire@gmail.com> schrieb im Newsbeitrag
news:1106671007.050300.117620@z14g2000cwz.googlegroups.com...
> robert,
>
> Class swapping was brought up on suby-ruby awhile ago under the topic
> of "Soul of a New Machine" (among others). The basic idea was an
> anology along the following lines:
>
> Lets say you have a Person, that preson does all the usual person
> things. But today that person has a job as a Fireman, so he goes to
> work and changes modes. Perhaps at night he goes home and must change
> modes again and be a Boyfriend (hopefully that means some fun methods
> are involved :wink:
>
> Okay so the notion is that an object can take on another level of
> abstraction, such that it's instance vars are essentially its "soul"
> and they can be embuded into different classes.
>
> It's sort of an inverted way to look the use of #become. But I think,

a

> much more undersandable and applicable way to look at.

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. Of course other
approaches are feasible, too. (Delegator instances that extend the role
module for example.)

Illustration:

require 'delegate'

class Object
  def role(mod)
    (@roles ||= {})[mod] ||= SimpleDelegator.new(self).extend(mod)
  end

  def role?(mod)
    @roles and @roles.has_key? mod
  end

  def unrole(mod)
    @roles and @roles.delete(mod)
  end

  def roles
    @roles ? @roles.keys :
  end
end

class Foo
  attr_accessor :name
end

module Bar
  def xx() name() end
end

f = Foo.new
f.name = "George"
puts f.role? Bar
p f.role(Bar).xx()
puts f.role? Bar
p f.roles
f.unrole(Bar)
p f.roles
puts f.role? Bar

Regards

    robert

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?

T.

Robert Klemme wrote:

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

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

Csaba,

[quote]
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.

[quote]
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.

[quote]
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! :wink:
and no COOP.

[quote]
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? :slight_smile:

T.

Ignore this T., I just saw your reply to robert.

Zach

Trans wrote:

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

Trans wrote:

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.

"Trans" <transfire@gmail.com> schrieb im Newsbeitrag
news:1106680107.836198.191680@f14g2000cwb.googlegroups.com...

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

pp = Person.new

=> #<Person:0x10189190>

pp.name = "Gavagai"

=> "Gavagai"

pp.role(Role1).work()

r1: Gavagai
=> nil

pp.role(Role2).work()

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

roles.rb (866 Bytes)

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?

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 :slight_smile: ). If its problematic,
raise an error.

Csaba

···

On 2005-01-26, ts <decoux@moulon.inra.fr> wrote:

Guy Decoux

Ignoring request to ignore :wink: [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.

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

Well, FYI, I guess.

Csaba Henk wrote:

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 :slight_smile: ). 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

class C
   uninclude Kernel
end

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

You have removed Kernel to *all* classes

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

this is not the same.

Guy Decoux

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

Zach Dennis wrote:

···

Csaba Henk wrote:

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 :slight_smile: ). 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

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

···

On 2005-01-27, ts <decoux@moulon.inra.fr> wrote:

class C
   uninclude Kernel
end

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

You have removed Kernel to *all* classes