From: Jörg W Mittag on
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.

jwm
From: Gary Wright on

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.

Gary Wright
From: Jörg W Mittag on
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.

jwm
From: Rick DeNatale on
2010/6/22 Jörg W Mittag <JoergWMittag+Ruby(a)googlemail.com>:
> 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.

> 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⟧

Some would quibble with calling this an anonymous class instead of an
anonymous module, but that's a quibble.

> 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.

There's a lot of truth in this, but I think there are some subtleties.

Mixing in a class like this would support inheriting instance methods
from the mixed-in class, but not class methods. One of the
differences (and they are pretty small) between classes and modules in
Ruby is that if a class subclasses another, the subclass object
inherits the class methods from the superclass. Modules don't have
"super modules" so a mechanism for this isn't needed in the
implementation.

Now I suppose this could be handled by doing something similar to the
ancestry chain of the metaclass of the subclass, but personally, I
don't think this whole idea of mixing in classes is really that
attractive a feature.

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: http://github.com/rubyredrick
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

From: Caleb Clausen on
On 6/22/10, Jörg W Mittag <JoergWMittag+Ruby(a)googlemail.com> 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.

First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4
Prev: Doubt regarding Testing with RSpec.
Next: General Confusion