Project

General

Profile

Actions

Feature #7274

closed

UnboundMethods should be bindable to any object that is_a?(owner of the UnboundMethod)

Added by rits (First Last) about 12 years ago. Updated almost 11 years ago.

Status:
Rejected
Target version:
[ruby-core:48847]

Description

=begin
as a corollary, (({UnboundMethod}))s referencing the same method name on the same owner, should be equal

currently (({UnboundMethod}))s binding is determined by the class via which they were retrieved, not the owner

class Base; def foo; end end
class Sub < Base; end

base_foo = Base.instance_method :foo
sub_foo = Sub.instance_method :foo
sub_foo.bind(Base.new).call

(({sub_foo.owner})) is (({Base})) so there does not seem to be any reason why it's not safe for it to bind to an instance of (({Base})).

and there does not seem to be any reason for (({sub_foo})) and (({base_foo})) to be unequal, they both refer to the same method, (({foo})) on (({Base})).
=end


Files

bind.pdf (85.7 KB) bind.pdf marcandre (Marc-Andre Lafortune), 08/31/2013 10:42 AM
Actions #1

Updated by marcandre (Marc-Andre Lafortune) about 12 years ago

  • Tracker changed from Bug to Feature

Reposted as original text was not posted on ruby-core mailing list.

Updated by marcandre (Marc-Andre Lafortune) about 12 years ago

  • Category set to core

I agree.

Quite simple: https://github.com/marcandre/ruby/compare/marcandre:trunk...marcandre:bind_with_owner
Diff: https://github.com/marcandre/ruby/compare/marcandre:trunk...marcandre:bind_with_owner.diff

note that make test and test-all already pass with your proposed behavior. I see no reason why this could cause an incompatibility either.

Moving to "feature", as the current behavior is intended, as the current doc shows.

If this behavior is accepted, we could also revisit the == (and the inspect method too), as where the method was taken would become irrelevant, only the point of definition would be important. I believe this should be the case too.

Any objection for bind to use the actual owner?

Updated by mame (Yusuke Endoh) about 12 years ago

  • Status changed from Open to Assigned
  • Assignee set to matz (Yukihiro Matsumoto)
  • Target version set to 2.6
Actions #5

Updated by matz (Yukihiro Matsumoto) over 11 years ago

  • Status changed from Assigned to Rejected

It is due to implementation limitation of CRuby.

The structure of instances of subclass (TT_XXX) may be different from superclasses.
In that case, the C implemented methods would crash.

So we prohibit them conservatively.

Matz.

Updated by marcandre (Marc-Andre Lafortune) over 11 years ago

matz (Yukihiro Matsumoto) wrote:

The structure of instances of subclass (TT_XXX) may be different from superclasses.
In that case, the C implemented methods would crash.

I'm not sure I understand exactly what you are saying, but I am sure that the proposed patch would not cause crashes. Do you had a concrete example in mind?

Note that the request is not about binding methods across unrelated classes or going from a subclass up to a superclass (both of which could create crashes). It is only about using the actual owner instead of the class used to access it.

Can you explain why, for example, Kernel#dup can be bound to a String, but not if it was accessed using Array, even though the exact same code would be executed?

Kernel.instance_method(:dup).bind("hello") # => accepted
Array.instance_method(:dup).bind("hello")  # => raises error, but really this is the same method.

Updated by rits (First Last) over 11 years ago

marcandre (Marc-Andre Lafortune) wrote:

matz (Yukihiro Matsumoto) wrote:

The structure of instances of subclass (TT_XXX) may be different from superclasses.
In that case, the C implemented methods would crash.

I'm not sure I understand exactly what you are saying, but I am sure that the proposed patch would not cause crashes. Do you had a concrete example in mind?

Note that the request is not about binding methods across unrelated classes or going from a subclass up to a superclass (both of which could create crashes). It is only about using the actual owner instead of the class used to access it.

Can you explain why, for example, Kernel#dup can be bound to a String, but not if it was accessed using Array, even though the exact same code would be executed?

Kernel.instance_method(:dup).bind("hello") # => accepted
Array.instance_method(:dup).bind("hello")  # => raises error, but really this is the same method.

It's also not clear to me what the concern is, please explain.

Updated by nobu (Nobuyoshi Nakada) over 11 years ago

  • Description updated (diff)

Updated by matz (Yukihiro Matsumoto) over 11 years ago

I am not sure the intention of the patch in [ruby-core:48864], but

  • if the owner is a module
  • if the binding object is an instance of the owner

we can allow bind(), but I am not sure how much useful this relaxing is, especially the latter one.

Matz.

Updated by nobu (Nobuyoshi Nakada) over 11 years ago

Instance methods of modules can be bound on any objects, already.
It's a part of method transplanting.

Updated by rits (First Last) about 11 years ago

is this rejected?

Updated by matz (Yukihiro Matsumoto) about 11 years ago

@rits Yes, basically. Method transplanting from modules is already allowed though.

Matz.

Updated by rits (First Last) about 11 years ago

matz (Yukihiro Matsumoto) wrote:

@rits Yes, basically. Method transplanting from modules is already allowed though.

Matz.

ok, but you never really explained what purpose this restriction serves, it seems pretty arbitrary and illogical

Updated by matz (Yukihiro Matsumoto) about 11 years ago

@rits You haven't read my message above, have you?

It is due to implementation limitation of CRuby.

The structure of instances of subclass (TT_XXX) may be different from superclasses.
In that case, the C implemented methods would crash.

So we prohibit them conservatively.

Matz.

Updated by rits (First Last) about 11 years ago

matz (Yukihiro Matsumoto) wrote:

@rits You haven't read my message above, have you?

It is due to implementation limitation of CRuby.

The structure of instances of subclass (TT_XXX) may be different from superclasses.
In that case, the C implemented methods would crash.

So we prohibit them conservatively.

Matz.

Neither I nor marcandre understood what you were alluding to and asked for a clarification, but you just repeated without clarifying.

You appear to be saying, and please correct if that's wrong, that a method from a subclass can not safely be bound to an instance of a superclass. If so, please note, that is not what's being suggested. sub_foo, from the original example, is a reference to a method (foo) that is defined in the superclass, not the subclass.

Updated by rits (First Last) about 11 years ago

to continue, the above:

we are binding a method from Base to an instance of Base, and it's failing. Why? How can that possibly be unsafe? What difference does it make that the method was requested from a subclass of Base? It certainly does not change the fact that the method is from Base.

Updated by matz (Yukihiro Matsumoto) about 11 years ago

OK, I misunderstood something.

In case foo is implemented in Base as in the original example, I admit that it will not cause any serious problem.

But I still have small concern.
If you are sure foo is implemented in Base, you can retrieve foo from Base (not from Sub),
so that you don't have to worry about redefinition.

If you are not, the code will be fragile. It's a sign of bad code.

Thus, I'd like to ask you why you want to relax? Consistency? Any actual use-case?
If there's actual non trivial use-case, I'd say go.

Matz.

Updated by rits (First Last) about 11 years ago

matz (Yukihiro Matsumoto) wrote:

OK, I misunderstood something.

In case foo is implemented in Base as in the original example, I admit that it will not cause any serious problem.

But I still have small concern.
If you are sure foo is implemented in Base, you can retrieve foo from Base (not from Sub),
so that you don't have to worry about redefinition.

If you are not, the code will be fragile. It's a sign of bad code.

Thus, I'd like to ask you why you want to relax? Consistency? Any actual use-case?
If there's actual non trivial use-case, I'd say go.

I discovered the current behavior while playing around, learning about method binding in ruby, not via a bug in my code. I noticed in irb (to_s) that an unbound method was remembering the class from which it was requested, which struck me as pointless (what difference could it possibly make). Then I guessed it must be used for something (for no good reason) and sure enough, two unbound method objects referencing the same method on the same class were unequal because they happened to have been requested from different classes.

This made absolutely no sense, if unbound methods are to have value equality (which they do) it should be based on owner and method name. This led to the next absurdity of a method being unbindable to an instance of its owner.

I thought such nonsense warranted a fix, so I filed a bug, I suppose you can disagree, but at least now we are on the same page.

Updated by Anonymous about 11 years ago

unsubscribe

Updated by rits (First Last) about 11 years ago

Matz, whether you reject this or not, I still would like to understand the reason for current behavior, it's not an accident, someone deliberately coded this.

Updated by marcandre (Marc-Andre Lafortune) almost 11 years ago

Hi,

matz (Yukihiro Matsumoto) wrote:

OK, I misunderstood something.

In case foo is implemented in Base as in the original example, I admit that it will not cause any serious problem.

But I still have small concern.
If you are sure foo is implemented in Base, you can retrieve foo from Base (not from Sub),
so that you don't have to worry about redefinition.

If you are not, the code will be fragile. It's a sign of bad code.

Thus, I'd like to ask you why you want to relax? Consistency? Any actual use-case?
If there's actual non trivial use-case, I'd say go.

I agree. I can't really think of an actual use-case. I was asking for consistency's sake.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0