[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

how to simulate self-style delegation

Its Me

4/1/2005 6:44:00 AM

I have classes A and B.

class A
def x
"A::x "
end
def xy
self.x + "A::y"
end
end

class B
def initialize a
@a = a
end
def x
"B::x "
end
def method_missing ???
?? SELF-style delegate to @a
end
end

I want true SELF-style delegation from objects of class B to A. i.e. self
calls on an A are in the context of the delegating B (if any).

a = A.new
a.xy #=> "A::x A::y"

b = B.new a

b.xy #=> "B::x A::y"
# calls b.method missing
# calls a.xy
# calls b.x #=> "B::x"
# calls a.y #=> "A::y"
# return "B::x A::y"

How can I do this?

Thanks!


11 Answers

Robert Klemme

4/1/2005 11:28:00 AM

0


"itsme213" <itsme213@hotmail.com> schrieb im Newsbeitrag
news:y463e.17923$1H3.10469@tornado.texas.rr.com...
> I have classes A and B.
>
> class A
> def x
> "A::x "
> end
> def xy
> self.x + "A::y"
> end
> end
>
> class B
> def initialize a
> @a = a
> end
> def x
> "B::x "
> end
> def method_missing ???
> ?? SELF-style delegate to @a
> end
> end
>
> I want true SELF-style delegation from objects of class B to A. i.e.
self
> calls on an A are in the context of the delegating B (if any).
>
> a = A.new
> a.xy #=> "A::x A::y"
>
> b = B.new a
>
> b.xy #=> "B::x A::y"
> # calls b.method missing
> # calls a.xy
> # calls b.x #=> "B::x"
> # calls a.y #=> "A::y"
> # return "B::x A::y"
>
> How can I do this?

From all that I know you can't because you cannot do any of these:

- bind an instance method of A to B (unless B is a superclass of A)

- convert a method to a proc that is usable with instance_eval.

The question is: does it make sense to do that?

Kind regards

robert


Below's my test code.

class A
def x
"A::x "
end
def xy
self.x + "A::y"
end
end

class B
def initialize a
@a = a
end
def x
"B::x "
end
# def respond_to?(*x) true end
def method_missing(sym, *args, &bl)
ppp = @a.method(sym).to_proc
# p ppp.arity
class<<self; self; end.class_eval { define_method(sym){ instance_eval
&ppp } }
# class<<self; self; end.class_eval { define_method(sym){ p self } }
# p respond_to?( sym )
send(sym,args)
# self.instance_eval &ppp
# instance_eval { ppp.call(*args) }
end
end

b=B.new A.new
# set_trace_func lambda {|*a| p a}
p b.xy



Its Me

4/1/2005 4:46:00 PM

0

"Robert Klemme" <bob.news@gmx.net> wrote

> From all that I know you can't because you cannot do any of these:
>
> - bind an instance method of A to B (unless B is a superclass of A)
>
> - convert a method to a proc that is usable with instance_eval.
>
> The question is: does it make sense to do that?

How do you mean "make sense"? Does my desired behavior make sense (it does
to me)? Or does it make sense for Ruby to permit one of the 2 approaches you
suggest (#2 does, not sure I understood #1)?

Thanks.

btw, my guess at what the instance_eval version might look like:

class B
def initialize delegatee
@a = delegatee
end
def method_missing sym, *args, &block
m = @a.method sym
self.instance_eval m, *args, &block
end
end



Robert Klemme

4/1/2005 9:32:00 PM

0


"itsme213" <itsme213@hotmail.com> schrieb im Newsbeitrag
news:NUe3e.22640$1H3.7060@tornado.texas.rr.com...
> "Robert Klemme" <bob.news@gmx.net> wrote
>
>> From all that I know you can't because you cannot do any of these:
>>
>> - bind an instance method of A to B (unless B is a superclass of A)
>>
>> - convert a method to a proc that is usable with instance_eval.
>>
>> The question is: does it make sense to do that?
>
> How do you mean "make sense"?

"Does it make sense to steal a method from a class and make it execute on
another classes instance?" I mean, you cannot generally expect that all
methods the "stolen" method requires are present in the other class. (I
know that's why you use the fallback but then again, what does one gain?
You can, for exaple, inject methods temporarily into the other instance.)

> Does my desired behavior make sense (it does to me)?

Apparently. :-) But in what use case do you need this? Although Ruby is
amazingly dynamic not all features we can think of actually make sense.

> Or does it make sense for Ruby to permit one of the 2 approaches you
> suggest (#2 does, not sure I understood #1)?
>
> Thanks.
>
> btw, my guess at what the instance_eval version might look like:
>
> class B
> def initialize delegatee
> @a = delegatee
> end
> def method_missing sym, *args, &block
> m = @a.method sym
> self.instance_eval m, *args, &block
> end
> end

Won't work as far as my experiements seem to indicate.

Here's another idea: how about copying instance state around?

Kind regards

robert

Its Me

4/2/2005 2:45:00 AM

0

"Robert Klemme" <bob.news@gmx.net> wrote

> "Does it make sense to steal a method from a class and make it execute on
> another classes instance?" I mean, you cannot generally expect that all
> methods the "stolen" method requires are present in the other class. (I
> know that's why you use the fallback but then again, what does one gain?
> You can, for exaple, inject methods temporarily into the other instance.)

I think this is how delegation is used in object-based languages like SELF.
It's a way for B to dynamically inherit at the object-level from A, both
behavior and state. Copying behavior would need copying state, which may not
be practical (how deep to copy, what about state change, etc.).

> > Does my desired behavior make sense (it does to me)?
>
> Apparently. :-) But in what use case do you need this? Although Ruby is
> amazingly dynamic not all features we can think of actually make sense.

I want to wrap one object (B) around another (A). B should get all of A's
behavior and state, customized by any methods on B. The behavior and state
of the A should be unchanged as it's methods may be invoked independently.

My specific use case needs a wide variety of ways of merging object graphs
(a bit like Joel's SuperHash, but deep + broad graphs and full control over
inheritance and merge rules). Given graphs g1,g2, I want to create their
merge g3:
g3.delegate_to(g1, g2)
Then any property on g3 is either defined by a method on g3, or determined
by the logic of method_missing on g3. Which would delegate to g1, g2 and
merge their results ... with the CAVEAT that g1,g2 recursive calls for other
properties would always try g3 first.

Don't know if I explained it well. I'm sure I can find other approaches too,
like passing an explicit and optional _delegator_ parameter to all calls. I
was curious how well I could emulate SELF-like delegation as it would fit
very well.

> > btw, my guess at what the instance_eval version might look like:
> >
> > class B
> > def initialize delegatee
> > @a = delegatee
> > end
> > def method_missing sym, *args, &block
> > m = @a.method sym
> > self.instance_eval m, *args, &block
> > end
> > end
>
> Won't work as far as my experiements seem to indicate.

Correct. I meant "my guess at what the instance_eval version might look like
IF instance_eval worked for rebinding self for methods".

> Here's another idea: how about copying instance state around?

I would have to be very careful with that, due to state changes.

Thanks for the ideas!


ES

4/2/2005 3:12:00 AM

0


Con fecha 2/4/2005, "itsme213" <itsme213@hotmail.com> escribió:
>"Robert Klemme" <bob.news@gmx.net> wrote
>
>> "Does it make sense to steal a method from a class and make it execute on
>> another classes instance?" I mean, you cannot generally expect that all
>> methods the "stolen" method requires are present in the other class. (I
>> know that's why you use the fallback but then again, what does one gain?
>> You can, for exaple, inject methods temporarily into the other instance.)
>
>I think this is how delegation is used in object-based languages like SELF.
>It's a way for B to dynamically inherit at the object-level from A, both
>behavior and state. Copying behavior would need copying state, which may not
>be practical (how deep to copy, what about state change, etc.).
>
>> > Does my desired behavior make sense (it does to me)?
>>
>> Apparently. :-) But in what use case do you need this? Although Ruby is
>> amazingly dynamic not all features we can think of actually make sense.
>
>I want to wrap one object (B) around another (A). B should get all of A's
>behavior and state, customized by any methods on B. The behavior and state
>of the A should be unchanged as it's methods may be invoked independently.
>
>My specific use case needs a wide variety of ways of merging object graphs
>(a bit like Joel's SuperHash, but deep + broad graphs and full control over
>inheritance and merge rules). Given graphs g1,g2, I want to create their
>merge g3:
> g3.delegate_to(g1, g2)
>Then any property on g3 is either defined by a method on g3, or determined
>by the logic of method_missing on g3. Which would delegate to g1, g2 and
>merge their results ... with the CAVEAT that g1,g2 recursive calls for other
>properties would always try g3 first.
>
>Don't know if I explained it well. I'm sure I can find other approaches too,
>like passing an explicit and optional _delegator_ parameter to all calls. I
>was curious how well I could emulate SELF-like delegation as it would fit
>very well.
>
>> > btw, my guess at what the instance_eval version might look like:
>> >
>> > class B
>> > def initialize delegatee
>> > @a = delegatee
>> > end
>> > def method_missing sym, *args, &block
>> > m = @a.method sym
>> > self.instance_eval m, *args, &block
>> > end
>> > end
>>
>> Won't work as far as my experiements seem to indicate.
>
>Correct. I meant "my guess at what the instance_eval version might look like
>IF instance_eval worked for rebinding self for methods".
>
>> Here's another idea: how about copying instance state around?
>
>I would have to be very careful with that, due to state changes.

I'm not familiar with Self so I'm somewhat at loss as to
what you're gaining with this approach.

Perhaps I'm missing a crucial point here. Should a B 'be' an A
in the normal is_a sense? Should B have access to A's state?
Something more than what SimpleDelegator offers?

method_missing provides a good way if B doesn't need to access
A's state. If such access is desired, for example to implement
a method on B that uses A's state, B can still access A's
instance variables etc.

>Thanks for the ideas!

E



Jeremy Tregunna

4/2/2005 8:15:00 AM

0

itsme213 wrote:
> "Robert Klemme" <bob.news@gmx.net> wrote
>
>
>>"Does it make sense to steal a method from a class and make it execute on
>>another classes instance?" I mean, you cannot generally expect that all
>>methods the "stolen" method requires are present in the other class. (I
>>know that's why you use the fallback but then again, what does one gain?
>>You can, for exaple, inject methods temporarily into the other instance.)
>
>
> I think this is how delegation is used in object-based languages like SELF.
> It's a way for B to dynamically inherit at the object-level from A, both
> behavior and state. Copying behavior would need copying state, which may not
> be practical (how deep to copy, what about state change, etc.).


I'm not 100% sure about self, but some languages that are inspired from
self seem to have similar behavior in this department, wherein you
aren't actually copying anything; its all done via a prototype lookup
chain. Which means, for example, that slot "foo" is not found in object
"B", but it exists in object "A" which "B" was cloned from, "B foo"
would be a valid message send because it would find the method as part
of the lookup chain, "foo" is not copied from "A" to "B" (though you
could if you really wanted too, but why bother? this is something that's
done for free in a similar manner). (Just to clarify.)

> Don't know if I explained it well. I'm sure I can find other approaches too,
> like passing an explicit and optional _delegator_ parameter to all calls. I
> was curious how well I could emulate SELF-like delegation as it would fit
> very well.

In Ruby? Not very easily in any way I can think of without doing some
butchering, and dirty hacks (this isn't to say doing what you want isn't
possible, I'm just not entirely sure, so I'll refrain from commenting
further).

--
Jeremy Tregunna


Csaba Henk

4/2/2005 4:48:00 PM

0

On 2005-04-01, itsme213 <itsme213@hotmail.com> wrote:
> I have classes A and B.
>
> class A
> def x
> "A::x "
> end
> def xy
> self.x + "A::y"
> end
> end
>
> class B
> def initialize a
> @a = a
> end
> def x
> "B::x "
> end
> def method_missing ???
> ?? SELF-style delegate to @a
> end
> end
>
> I want true SELF-style delegation from objects of class B to A. i.e. self
> calls on an A are in the context of the delegating B (if any).
>
> a = A.new
> a.xy #=> "A::x A::y"
>
> b = B.new a
>
> b.xy #=> "B::x A::y"
> # calls b.method missing
> # calls a.xy
> # calls b.x #=> "B::x"
> # calls a.y #=> "A::y"
> # return "B::x A::y"
>
> How can I do this?

What I can imagine:

1)

* create a joint proxy object for a and b (something along the lines of
the cuckoo of
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-t...,
just the method_missing of the proxy should look at both a and b)

* copy all instance_variables of b to the proxy

* define a method_missing for b which resends the message :xy to the
proxy object (somehow you should exclude circular method_missing calls
between b and the proxy)

This work by-and-large as long as you don't overwrite instance variables
in b. If you have an instance variable which gets overwritten
frequently, make a custom setter for it which updates the proxy as well.

or

2)

* Make A a module which extends itself

* define A.new similarly to this:

def new
a = Module.new
a.send :include, self
a.extend a
end

Now A quacks like a class.

* in B.initialize, extend the instance with a.

However, it pulls in the whole A "instance", and delegation is not
limited to #xy.

Csaba

Its Me

4/2/2005 5:32:00 PM

0


"Saynatkari" <ruby-ml@magical-cat.org> wrote in message

> Perhaps I'm missing a crucial point here. Should a B 'be' an A
> in the normal is_a sense? Should B have access to A's state?
> Something more than what SimpleDelegator offers?

SELF-style delegation is for "inheriting" at the _object_ level. It lets me
dynamically change the object that b1 delegates to.
b1 = B.new
b1.delegate_to(a1)
...stuff
b1.delegate_to(a2)

So
class B < A; end
does not help.

SimpleDelegator simply forwards missing methods, it does not provide the
binding of self that I am looking for. Here is another example with desired
behavior at the end.

class Role
def initialize(type)
@type = type
end
def name
"unbound"
end
def describe
"I am #{self.name}, a #{@type}"
end
end

PM = Role.new("Project Manager")
GA = Role.new("Graphic Artist")

class Person
attr_accessor :name
def delegate_to
?? method_missing and self_binding magic here
end
end

JOE = Person.new
JOE.name = "Joe"

JOE.delegate_to(PM)
JOE.describe #=> "I am Joe, a Project Manager"

JOE.delegate_to(GA)
JOE.describe #=> "I am Joe, a Graphic Artist"

Hope that is more clear. Maybe this is not possible in Ruby ... but I just
got a pointer to _evil_ ruby, perhaps that might have something?



Pit Capitain

4/6/2005 1:17:00 PM

0

itsme213 schrieb:
> My specific use case needs a wide variety of ways of merging object graphs
> (a bit like Joel's SuperHash, but deep + broad graphs and full control over
> inheritance and merge rules). Given graphs g1,g2, I want to create their
> merge g3:
> g3.delegate_to(g1, g2)

I'm also interested in this subject. What I'm trying to do is to join
object graphs that don't have to know about each other. I call the
medium between the object graphs "seams" (it rhymes with "object teams",
see [1] and [2]). As yet, I have coded the basics only, some AOP like
functions. With object seams, the method "delegate_to" in your
role/person example could be coded as

require "seam"

class Person
def delegate_to(role)
@seam.clear if @seam
@seam = Seam.new do |s|
s.instead_of(self, :describe) do role.describe end
s.instead_of(role, :name) do self.name end
end
end
end

Send me a mail, if you're interested, and I'll add the preliminary code
to my rubyforge project (which is still empty).

Regards,
Pit

[1] http://www.objec...
[2] http://www.objec.../software.html#ROT


Its Me

4/7/2005 12:59:00 AM

0


"Pit Capitain" <pit@capitain.de> wrote in message

> class Person
> def delegate_to(role)
> @seam.clear if @seam
> @seam = Seam.new do |s|
> s.instead_of(self, :describe) do role.describe end
> s.instead_of(role, :name) do self.name end
> end
> end
> end

Interesting. I'll give it some thought.

Would this require knowledge of the internal structure of role#describe? Or
would it be safe to do the following?
(s.methods - Object.instance_methods).each {|m|
s.instead_of(role, m.intern) do self.send(m) end
}

Thanks.