Feature #1586

Including a module already present in ancestors should not be ignored

Added by bitsweat (Jeremy Kemper) almost 3 years ago. Updated 2 months ago.

[ruby-core:23740]
Status:Assigned Start date:06/07/2009
Priority:Normal Due date:
Assignee:matz (Yukihiro Matsumoto) % Done:

0%

Category:core
Target version:2.0.0

Description

The scenario:
* I include Foo in Numeric to provide #bar
* Some other library includes a module in Float to provide #bar
* So I include Foo in Float to use my #bar
* But including Foo in Float is ignored since it's already in the ancestor chain

I think it should be added to the ancestor chain, even if it's already present, since I may want to override some other method earlier in the ancestor chain.

# Including a module already included in a superclass is ignored
>> module Foo; end
=> nil
>> class Numeric; include Foo; end
=> Numeric
>> Float.ancestors
=> [Float, Precision, Numeric, Foo, Comparable, Object, Kernel]
>> class Float; include Foo; end
=> Float
>> Float.ancestors
=> [Float, Precision, Numeric, Foo, Comparable, Object, Kernel]

# Reversing the order of inclusion works as expected
>> module Foo; end
=> nil
>> class Float; include Foo; end
=> Float
>> Float.ancestors
=> [Float, Foo, Precision, Numeric, Comparable, Object, Kernel]
>> class Numeric; include Foo; end
=> Numeric
>> Float.ancestors
=> [Float, Foo, Precision, Numeric, Foo, Comparable, Object, Kernel]

# And so does including a dupe of the existing module in the subclass
>> module Foo; end
=> nil
>> class Numeric; include Foo; end
=> Numeric
>> Float.ancestors
=> [Float, Precision, Numeric, Foo, Comparable, Object, Kernel]
>> class Float; include Foo.dup; end
=> Float
>> Float.ancestors
=> [Float, #<Module:0x19bcd40>, Precision, Numeric, Foo, Comparable, Object, Kernel]

History

Updated by nobu (Nobuyoshi Nakada) almost 3 years ago

  • Category set to core
  • Assignee set to matz (Yukihiro Matsumoto)
  • Target version set to 3.0

Updated by RickDeNatale (Rick DeNatale) almost 3 years ago

Actually, for a while, back in 2006, Ruby 1.9 (in its experimental form) used to do just what this ticket asks for:

http://talklikeaduck.denhaven2.com/2006/10/09/a-subtle-change-to-mixin-semantics-in-ruby-1-9

However, that change got reverted.

I asked Matz why at RubyConf 2007, and documented our conversion

http://talklikeaduck.denhaven2.com/2007/11/03/a-chat-with-matz-classs-variable-reversion-and-a-mystery-explained

The problem is that MRI, and I guess YARV doesn't keep track of where in the chain of classes, and module proxies it found the currently executing method, so super is implemented by doing a method search starting with the klass of self, and proceding until the method is found a SECOND time.  With this implementation it's easier to turn module re-inclusion into a nop than to deal with the consequences.

Updated by bitsweat (Jeremy Kemper) almost 3 years ago

Fascinating. Thanks for the history behind this, Rick.

Despite the implementation difficulties, I'd like to see this choice revisited for a future Ruby. I consider it a bug.

Updated by shyouhei (Shyouhei Urabe) over 1 year ago

  • Status changed from Open to Assigned

Updated by naruse (Yui NARUSE) 7 months ago

  • Project changed from ruby-trunk to 14
  • Category deleted (core)
  • Target version deleted (3.0)

Updated by naruse (Yui NARUSE) 7 months ago

  • Project changed from 14 to ruby-trunk

Updated by mame (Yusuke Endoh) 3 months ago

  • Status changed from Assigned to Rejected
I'm rejecting this feature ticket because no progress has been made for a long time. See [ruby-core:42391]. This is indeed a hard issue. There are two approaches to make it consistent: A) prohibit multiple appearances of one module in ancestors B) permit multiple appearances I guess matz prefers A to B, so B will not be admissible. (just my guess, though) The current behavior aims A, but is indeed incomplete. In casual use case, however, module inclusion is used statically, i.e., used when a class is first defined. Thus, no actual problem is caused in practical case, I think. In addition, A will be very hard to implement completely. Even if it is possible, I'm afraid that the behavior will be bug-prone (for both user code and ruby impl), because it means that an already-included module may be later removed from ancestors, unexpectedly. Thus, I think that we can not fix this elegantly and will not change the current behavior. But we might have to do something if there is any *concrete* scenario in that the current behavior causes a trouble. Let us know such a scenario if you have. -- Yusuke Endoh <mame@tsg.ne.jp>

Updated by ko1 (Koichi Sasada) 3 months ago

  • Category set to core
  • Status changed from Rejected to Open
  • Target version set to 2.0.0
I want to change this behavior as permitting multiple modules in an ancestors list (option B). Can I do it? (If I can, I'll implement it after this Apr).

Updated by marcandre (Marc-Andre Lafortune) 3 months ago

Koichi Sasada wrote: > I want to change this behavior as permitting multiple modules in an ancestors list (option B). > > Can I do it? (If I can, I'll implement it after this Apr). That would be great. I'm assuming it be allowed to include a module multiple times in a row too? module M def to_s "*" + super + "*" end end 3.times{ Fixnum.prepend(M) } 42.to_s # => "***42***"

Updated by shyouhei (Shyouhei Urabe) 2 months ago

  • Status changed from Open to Assigned

Also available in: Atom PDF