Rick DeNatale | 15 May 22:24

Re: Method lookup for modules included in modules

On Thu, May 15, 2008 at 10:59 AM, Mark Wilden <mark <at> mwilden.com> wrote:
> On May 14, 2008, at 10:05 PM, ara.t.howard wrote:
>
>>> Here's my problem. If A1 doesn't contain the method, what causes the
>>> search to backtrack to the B proxy and follow its super pointer to the C
>>> proxy? This "backtracking" seems counter to the purported "linear" lookup
>>> order. (This is just a conceptual problem for me - it's obvious what the
>>> lookup order is, and it's easy to verify.)
>>
>> as each module in included in the target, it's entire lookup chain is
>> added to that target - therefore no 'backtracking' is required, we just end
>> up with a long line that, when followed, happens to give the appearance if
>> backtracking.
>
> I do understand that that's how it works in practice. I was trying to
> understand the process in terms of its implementation via proxies, klass,
> super and m_tbl. If it were truly implemented as a single long chain, then
> that would require (in my example) that the A2 proxy's super points to the B
> proxy. Hmmm....I wonder if that's indeed the case, as you imply?

module A1; end;
module A2; end;
module B; include A1; end
module C; include A2; end
class D; include C; include B; end

results in the chains:

A1
A1
B -> proxy1(A1)
C-> proxy2(A2)
D->proxy5(B)->proxy6(A1)->proxy3(C)->proxy4(A2)->Object->proxy0(Kernel)

where proxyn indicates the nth module-proxy in creation order.

When you include a module in a class, the current chain is searched to
see if the module is already included and doesn't re-include it, and
includes in a given class or module executed later are inserted before
earlier includes in the same class or module, so the chain for the
original set of modules and classes:

module A; end;
module B; include A; end
module C; include A; end
class D; include B; include C; end

The chain for D looks like this:

D->proxy(C)->proxy(B)->proxy(A)->Object->proxy(Kernel)

This can lead to some small surprises for some people.  Ruby doesn't
include a proxy to a given module more than once in any inheritance
chain, because of the way it implements finding overridden methods
when resolving super.  MRI doesn't keep track of the place in the
chain where it found the currently executing method, so to find the
method to be invoked for super, it searches again from the start until
it finds the current METHOD, then searches from the next link in the
chain.  If a module proxy were repeated then this wouldn't work
because some cases would loop.  See the two articles below:

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

--

-- 
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/


Gmane