From: Caleb Clausen Date: 2010-06-24T11:27:09+09:00 Subject: Re: Namespacing a class On 6/22/10, Jörg W Mittag wrote: > Gary Wright wrote: >> On Jun 21, 2010, at 3:35 PM, Jörg W Mittag wrote: >>> Eric MSP Veith wrote: >>>> Modules are used for mixins. Is it possible -- and does it make sense at >>>> all >>>> - -- to mixin a class into another class? I guess not? >>> Actually, to add a bit to Brian's answer: it *is* possible and makes >>> perfect sense. It's just not allowed in Ruby, but it *is* allowed in >>> other languages. >> Allowing classes to mixin other classes would change the class >> hierarchy from a tree to a directed acyclic graph. As soon as you >> do that you have to figure out how to manage the method lookup >> process along multiple paths. I don't think there is any sort of >> consensus on how to do that (i.e. different languages take >> different approaches) and so I'm not sure 'makes perfect sense' is >> an accurate characterization of the solution space for that >> problem. > > You can take the algorithm Ruby uses for mixins and simply substitute > classes for modules and it would work exactly the way it does today, > including the linearization property. > > The way mixins work in Ruby is (roughly) as follows (assume that we > want to mix ⟦M⟧ into a class ⟦C⟧, ignore for a moment the case of > mixing a mixin into another mixin): > > 1. create an anonymous class ⟦I⟧ > 2. set ⟦I⟧'s method table pointer to ⟦M⟧'s method table > 3. set ⟦I⟧'s superclass to the ⟦C⟧'s singleton class's superclass > 4. set ⟦C⟧'s singleton class's superclass to ⟦I⟧ > > In other words: create an anonymous class with the same methods but a > different identity as the mixin and insert it in the ancestor chain > directly above the singleton class. > > There's really no need here for ⟦M⟧ to be a module, it could just as > well be a class. The linearization property depends on the fact that > mixin inclusion is done via a freshly minted anonymous class which has > no ancestors or descendants of its own: the include class ⟦I⟧ *only* > gets the methods from the mixin, it does not get its place in the > inheritance hierarchy. Whether the method table that ⟦I⟧ points to > used to belong to a module or a class is really irrelevant. It's just > a bag of methods. > > The case of mixing in a mixin into another mixin is also handled > similar to the way it is today: currently, when you mix a module into > a module, it gets inserted above the module like it would above a > class. And then, when you mix a module ⟦M⟧ into a *class*, Ruby not > only constructs an include class for the ⟦M⟧'s method table, but > recursively also for all modules mixed into ⟦M⟧. Note that this means > that this mixin chain is only walked *once*, when you include ⟦M⟧ into > the class. If you later mix other modules into ⟦M⟧, their methods will > *not* be available to instances of the class. > > Again, it would be possible to do just the same thing with classes > instead of modules: walk the chain of include classes and simply clone > them, then inject them into the ancestors chain like above. > > That's what I meant by "makes perfect sense": you can take the exact > same algorithm that Ruby currently uses for module inclusion and use > it for class inclusion with the exact same results. What you describe might work well enough for pure-ruby classes, but if you tried to mix a core class (Array, String) into another class, the result would be segfaults. Maybe the same would be true of any class implemented in C? Not sure.