From: Rick DeNatale Date: 2007-03-03T01:25:11+09:00 Subject: Re: Module re-inclusion in 1.9 vs 1.8 On 3/2/07, Pit Capitain wrote: > Rick DeNatale schrieb: > > Some months ago I noticed that the semantics of module inclusion had > > changed in 1.9 so that if a subclass reincluded a module which was > > included by one of its superclasses the module would be reinserted in > > the inheritance chain. > > (...) > > Today I refreshed 1.9 and noticed that this seems to have gone away: > > (...) > > Was this a conscious decision? Personally I would argue for the > > previous semantics. > > FWIW, if the module inclusion semantics would be changed to allow > multiple occurrences of a module in the inheritance chain, we should be > able to fix the following problem, mentioned for example in [ruby-core:2013] > > module EnumerableAddOns > def sum > inject(0) { |s, n| s += n } > end > end > > module Enumerable > include EnumerableAddOns > end > > p [ 1, 2, 3, 4 ].sum # => undefined method `sum' (NoMethodError) This is a different issue I believe. What's happening here, as far as I can see is that when you include a module into another module or class, a chain of pseudo-classes is created which parallels the ancestor chain of the included module. Note that this ancestor chain can only include modules since modules can only have modules as super. The implementation marks each of these pseudo classes as a T_ICLASS (included class). Each of these pseudo-class points to the method and iv_tables of the associated module. The whole chain is then inserted between the including class and it's existing super. So if you change the methods of the module, or add a class instance variable, this affects all of the classes/modules including it since the pseudo-classes all reference the method and iv tables, but including another module in a module has no effect on existing inclusions of that module. To make that work would require keeping track of, or searching for all existing inclusions of a module and relinking the super chain when a change was made to which modules it includes. There are a few other quirks like this in Ruby, where some changes don't affect prior history. I was just told about one last night at our local ruby brigade hack night, by Nathaniel Talbott relative to class variables: rick@frodo:/public/rubyscripts$ cat civtest.rb class C1; end class C2 < C1; @@c = :c2; p @@c; end class C1; @@c = :c1; p @@c; end class C2; p @@c; end rick@frodo:/public/rubyscripts$ ruby1.8 civtest.rb :c2 :c1 :c2 rick@frodo:/public/rubyscripts$ ruby1.9 civtest.rb :c2 :c1 :c2 Note that had @@c been created in the superclass first, it would have been the same variable as in the subclass. Now the issue which prompted this thread is that when a module is included, the implementation searches up the existing anscestor chain, and if it finds a pseudo-class which represents the module being included; which it determines by comparing the method table pointers; it ignores the request to re-include the module. 1.9 used to allow re-inclusion but the current version doesn't. It seems to me that this would be a safe change, at least from the semantics, since existing code shouldn't be using re-inclusion since it doesn't work, and it could be useful. -- Rick DeNatale My blog on Ruby http://talklikeaduck.denhaven2.com/