From: Xavier Noria Date: 2011-04-12T17:53:23+09:00 Subject: [ruby-core:35720] Re: caching of the ancestor chain On Mon, Apr 11, 2011 at 1:08 AM, Charles Oliver Nutter wrote: > Am I misunderstanding the original question? Since MRI inserts proxies in the ancestor chain that point to the actual modules and adjusts the parent pointers to have a linear ancestor chain (by linear I mean that you follow the pointers up and you're done), you get this module M end class C include M end module N def doesnt p :doesnt end end module M include N def works p :works end end p C.ancestors # => [C, M, Object, Kernel, BasicObject] p M.ancestors # => [M, N] That linear ancestor chain is not updated if the ancestors chain of involved modules is modified. That's what I meant by "caching". Both the #works method and the N module augment the M module after inclusion in C. Thanks to the pointer to M in the ancestor chain (via the proxy) instances of C respond to #works. But because of the implementation N does not belong to the ancestor chain of C, while it belongs to the ancestor chain of M. In consequence C.new.works C.new.doesnt raises an exception in the second line. I think that module proxies are implementation, and that conceptually they do not belong to the object model (please correct me if that's wrong!). So, conceptually, mixin methods are not copied into classes, modules are "linked" to classes and inspected at call time to offer a method dispatch that supports dynamic stuff as shown above. But that metaphor does not extend to modules mixed-in later. Question was whether this was a conscious design/implementation decision on behalf of speed. Charles I know you know all of this, just wanted to clarify my question :).