Can't bind a singleton method to a subclass?

D

David Walker

The behavior of this snippet doesn't make sense to me:

class A
def A.class_method
puts "Class method worked in #{self}"
end
end

class B < A; end

aklass = (class << A; self; end)
bklass = (class << B; self; end)

p RUBY_VERSION

A.class_method
B.class_method

p A.method:)class_method)
p B.method:)class_method)
p aklass.instance_method:)class_method)
p bklass.instance_method:)class_method)
p A.method:)class_method).unbind
p B.method:)class_method).unbind
p A.method:)class_method).unbind.bind(A)
p B.method:)class_method).unbind.bind(B)

The output (for me) is:

"1.8.4"
Class method worked in A
Class method worked in B
#<Method: A.class_method>
#<Method: B(A).class_method>
#<UnboundMethod: #<Class:A>#class_method>
#<UnboundMethod: #<Class:A>#class_method>
#<UnboundMethod: #<Class:A>#class_method>
#<UnboundMethod: #<Class:A>#class_method>
#<Method: A.class_method>
/workplace2/test.rb:24:in `bind': singleton method called for a
different object (TypeError)
from /workplace2/test.rb:24

In my mind, the last call should succeed, instead of raising an error.
Is it just plain impossible to re-bind the method to yield a #<Method:
B(A).class_method>?
 
D

dblack

Hi --

The behavior of this snippet doesn't make sense to me:

class A
def A.class_method
puts "Class method worked in #{self}"
end
end

class B < A; end

aklass = (class << A; self; end)
bklass = (class << B; self; end)

p RUBY_VERSION

A.class_method
B.class_method

p A.method:)class_method)
p B.method:)class_method)
p aklass.instance_method:)class_method)
p bklass.instance_method:)class_method)
p A.method:)class_method).unbind
p B.method:)class_method).unbind
p A.method:)class_method).unbind.bind(A)
p B.method:)class_method).unbind.bind(B)

The output (for me) is:

"1.8.4"
Class method worked in A
Class method worked in B
#<Method: A.class_method>
#<Method: B(A).class_method>
#<UnboundMethod: #<Class:A>#class_method>
#<UnboundMethod: #<Class:A>#class_method>
#<UnboundMethod: #<Class:A>#class_method>
#<UnboundMethod: #<Class:A>#class_method>
#<Method: A.class_method>
/workplace2/test.rb:24:in `bind': singleton method called for a
different object (TypeError)
from /workplace2/test.rb:24

In my mind, the last call should succeed, instead of raising an error.
Is it just plain impossible to re-bind the method to yield a #<Method:
B(A).class_method>?

The fact that B can call A.class_method is due to the special-casing
of singleton classes of Class objects; it's the one case where a given
object can execute singleton methods of another object. But those
singleton methods still belong, unambiguously, to the first object, A.
The reason B can call them is that A's singleton class serves as the
superclass of B's singleton class -- so class_method lies on the
method look-up path of B. But class_method is still defined only in
A's singleton class.


David

--
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
 
A

ara.t.howard

The fact that B can call A.class_method is due to the special-casing of
singleton classes of Class objects; it's the one case where a given object
can execute singleton methods of another object. But those singleton
methods still belong, unambiguously, to the first object, A. The reason B
can call them is that A's singleton class serves as the superclass of B's
singleton class -- so class_method lies on the method look-up path of B.
But class_method is still defined only in A's singleton class.

still, quiet odd that one can do this

meaning, it's strange that you can 'unbind' from a class something which
cannot be bound to it and yet must have been for the initial call to 'unbind'
to succeed - a contradiction.

interesting.

-a
 
D

David Walker

The fact that B can call A.class_method is due to the special-casing
of singleton classes of Class objects; it's the one case where a given
object can execute singleton methods of another object. But those
singleton methods still belong, unambiguously, to the first object, A.
The reason B can call them is that A's singleton class serves as the
superclass of B's singleton class -- so class_method lies on the
method look-up path of B. But class_method is still defined only in
A's singleton class.

Thanks for the info. That's mostly what I thought - I guess I should
have said, "This seems silly to me," not, "This doesn't make sense to
me." :)

How, though, is this a special case? For instance methods, you can
bind an instance method to any object who's class is <= the class the
method is defined on. Why should it be different for singletons? I.e.
I have an instance method (instance from the perspective of the
singleton class A), I should be able to bind it to an object who's
class is a subclass of the class the instance is defined on. If A's
singleton class is a superclass of B's singleton class (which it ought
to be) then this relation holds. I think. :)

In any case, "B.method:)class_method)" gave me a #<Method:
B(A).class_method>. My original question still stands: is there no way
to recover this binding once it's been unbound?
 
D

dblack

Hi --

Thanks for the info. That's mostly what I thought - I guess I should
have said, "This seems silly to me," not, "This doesn't make sense to
me." :)

How, though, is this a special case?

What I mean is: the fact that you can call B.class_method, even though
class_method is a singleton method of an object other than B, is a
special dispensation for class objects.
For instance methods, you can
bind an instance method to any object who's class is <= the class the
method is defined on. Why should it be different for singletons? I.e.
I have an instance method (instance from the perspective of the
singleton class A), I should be able to bind it to an object who's
class is a subclass of the class the instance is defined on. If A's
singleton class is a superclass of B's singleton class (which it ought
to be) then this relation holds. I think. :)

I guess it's just a matter of how the logic cascades. Forbidding a
rebinding of a singleton method on a different object takes precedence
over the special situation where class singleton methods are sort of
not really singleton (i.e., can be called by subclasses).
In any case, "B.method:)class_method)" gave me a #<Method:
B(A).class_method>. My original question still stands: is there no way
to recover this binding once it's been unbound?

Maybe, but not that I can think of.


David

--
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
 
D

dblack

Hi --

still, quiet odd that one can do this


meaning, it's strange that you can 'unbind' from a class something which
cannot be bound to it and yet must have been for the initial call to
'unbind'
to succeed - a contradiction.

I think it's not so much that you're unbinding it *from* B, as that
you're grabbing it *through* B. So, by the time you unbind it, it
doesn't "know" that you got it that way; it's just a Method object,
and it allows itself to be unbound. I'd say that's a side-effect of
the fact that A's singleton methods can be got at via B at all.

Going the other way, though, it does "know" that you're trying to bind
it to something other than the object in whose singleton class it's
defined.


David

--
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
 
A

ara.t.howard

I think it's not so much that you're unbinding it *from* B, as that you're
grabbing it *through* B. So, by the time you unbind it, it doesn't "know"
that you got it that way; it's just a Method object, and it allows itself to
be unbound.

yeah. at first i thought that, but:

harp:~ > cat a.rb
class A
def self.class_method() 42 end
end
class B < A; end

p A.method:)class_method)
p B.method:)class_method)


harp:~ > ruby a.rb
#<Method: A.class_method>
#<Method: B(A).class_method>

so it seems like the method is aware that it's receiver is B. but i take your
point.
I'd say that's a side-effect of the fact that A's singleton
methods can be got at via B at all.

yes. put another way, and something i was be-moaning several years ago, is
this:

harp:~ > cat a.rb
class A
SINGLETON_CLASS = class << self
self
end
end
class B < A
SINGLETON_CLASS = class << self
self
end
end

p B::SINGLETON_CLASS.ancestors

harp:~ > ruby a.rb
[Class, Module, Object, Kernel]

singleton classes should respect inheritence.

regards.

-a
 
D

dblack

Hi --

yeah. at first i thought that, but:

harp:~ > cat a.rb
class A
def self.class_method() 42 end
end
class B < A; end

p A.method:)class_method)
p B.method:)class_method)


harp:~ > ruby a.rb
#<Method: A.class_method>
#<Method: B(A).class_method>

so it seems like the method is aware that it's receiver is B. but i take
your point.

Interesting. The dot is the singleton-method indicator in that
notation:

irb(main):013:0> obj = Object.new
=> #<Object:0xb7f04aa8>
irb(main):014:0> def obj.x; end
=> nil
irb(main):015:0> obj.method:)x)
=> #<Method: #<Object:0xb7f04aa8>.x>

In your example, it's A.class_method and not
#<Class:0xabcdef>.class_method. But it still really means the
singleton class of A. I guess it's all in the service of allowing for
that special case singleton-of-Class-object thing.
I'd say that's a side-effect of the fact that A's singleton
methods can be got at via B at all.

yes. put another way, and something i was be-moaning several years ago, is
this:

harp:~ > cat a.rb
class A
SINGLETON_CLASS = class << self
self
end
end
class B < A
SINGLETON_CLASS = class << self
self
end
end

p B::SINGLETON_CLASS.ancestors

harp:~ > ruby a.rb
[Class, Module, Object, Kernel]

singleton classes should respect inheritence.

I'm not sure what you mean here. Is it that ancestors doesn't show
singleton classes?


David

--
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
 
A

ara.t.howard

yes. put another way, and something i was be-moaning several years ago, is
this:

harp:~ > cat a.rb
class A
SINGLETON_CLASS = class << self
self
end
end
class B < A
SINGLETON_CLASS = class << self
self
end
end

p B::SINGLETON_CLASS.ancestors

harp:~ > ruby a.rb
[Class, Module, Object, Kernel]

singleton classes should respect inheritence.

I'm not sure what you mean here. Is it that ancestors doesn't show
singleton classes?

i meant that B's singleton class should inherit from A's singleton class - the
fact that it does not leads to all sorts of suprising behaviour...


-a
 
D

dblack

Hi --

yes. put another way, and something i was be-moaning several years ago,
is
this:

harp:~ > cat a.rb
class A
SINGLETON_CLASS = class << self
self
end
end
class B < A
SINGLETON_CLASS = class << self
self
end
end

p B::SINGLETON_CLASS.ancestors

harp:~ > ruby a.rb
[Class, Module, Object, Kernel]

singleton classes should respect inheritence.

I'm not sure what you mean here. Is it that ancestors doesn't show
singleton classes?

i meant that B's singleton class should inherit from A's singleton
class - the fact that it does not leads to all sorts of suprising
behaviour...

I'm still not seeing where your example fits in with this (I assume
I'm just being thick and not seeing what's right in front of me) --
but I'll forge ahead and note that the question of the subclass
relation of singleton classes seems to have swung back and forth. In
1.8.2, this was true (given the usual singleton_class method):

B.singleton_class.superclass == A.singleton_class

and it's true in fairly recent 1.9, but not true in 1.8.5. I have no
idea why not. B.meth still looks in A's singleton class for "meth",
but apparently inheritance is not used as the mechanism.


David

--
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
 

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,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top