Project

General

Profile

Bug #5940

Resolve conflict between inheritance and mixins

Added by mlanza (Mario Lanza) over 8 years ago. Updated over 8 years ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
-
ruby -v:
1.9.2
Backport:
[ruby-core:42250]

Description

Some in the Ruby community (like Chad Fowler) have noticed issues around how Ruby attempts to transverse a method's pipeline up the inheritance chain. When you inherit a class from another class and then add a mixin, the mixin is not able to supercede the methods defined in the inherited-from class.

I've demonstrated the issue here:
https://gist.github.com/515856

It would be nice if Ruby were to change its default inheritance behavior so that mixin do override a class's methods. Essentially, the behavior would treat inheritance in nearly the same manner that it treats mixins.

class Person < ActiveRecord::Base
end

would be effectively treated, in a manner of speaking, as:

class Person
include ActiveRecord::Base
end

(I would go so far as to say that we get rid of direct inheritance in favor of always using mixins to accomplish our inheritance goals. I do have a reservation for how this would affect backwards compatibility.)

I dunno if Ruby's current behavior is intentional (having purposeful benefits) but I find it annoying as it makes it difficult to override a behavior in third-party libraries. When I own the classes, I can simply refactor them to prefer mixins as to avoid this issue, but this doesn't solve the dilemma when dealing with vendor libraries.

Updated by rue (Eero Saynatkari) over 8 years ago

On 28.01.2012, at 01:53, Mario Lanza wrote:

Some in the Ruby community (like Chad Fowler) have noticed issues around how Ruby attempts to transverse a method's pipeline up the inheritance chain. When you inherit a class from another class and then add a mixin, the mixin is not able to supercede the methods defined in the inherited-from class.

You mean the class itself, not an inherited class, as your example shows. An included module is inserted just above the class in the lookup chain.

I dunno if Ruby's current behavior is intentional (having purposeful benefits) but I find it annoying as it makes it difficult to override a behavior in third-party libraries. When I own the classes, I can simply refactor them to prefer mixins as to avoid this issue, but this doesn't solve the dilemma when dealing with vendor libraries.

It is intentional, and the change would breaking. Consider Array, for example: it implements #each, and by that contract, includes Enumerable. However, in addition to using the Enumerable methods, Array overrides some of them for performance reasons. Hash does it to return Hashes rather than Arrays, like Enumerable does, and so on. Proposed change would require changing that. In short, the problem would then be that you wouldn't be able to override module behaviour.

Arguably, the problem is about equivalent either way around, but there's no clear advantage to go changing things especially considering how much code would need to be changed.

There're several solutions to your dilemma of overriding behavior in third-party libraries, if you really wish to do so: you can extend individual objects with the module (extend includes into the singleton class); you can simply inherit and override; you can inherit and thereby create a slot for modules in-between the original and your subclass; you can use composition instead of inheritance, …

--

Updated by rkh (Konstantin Haase) over 8 years ago

What you want is a feature planned for Ruby 2.0 called Module#prepend (as compared to Module#include).

Konstantin

On Jan 28, 2012, at 00:53 , Mario Lanza wrote:

Issue #5940 has been reported by Mario Lanza.


Bug #5940: Resolve conflict between inheritance and mixins
https://bugs.ruby-lang.org/issues/5940

Author: Mario Lanza
Status: Open
Priority: Normal
Assignee:
Category:
Target version:
ruby -v: 1.9.2

Some in the Ruby community (like Chad Fowler) have noticed issues around how Ruby attempts to transverse a method's pipeline up the inheritance chain. When you inherit a class from another class and then add a mixin, the mixin is not able to supercede the methods defined in the inherited-from class.

I've demonstrated the issue here:
https://gist.github.com/515856

It would be nice if Ruby were to change its default inheritance behavior so that mixin do override a class's methods. Essentially, the behavior would treat inheritance in nearly the same manner that it treats mixins.

class Person < ActiveRecord::Base
end

would be effectively treated, in a manner of speaking, as:

class Person
include ActiveRecord::Base
end

(I would go so far as to say that we get rid of direct inheritance in favor of always using mixins to accomplish our inheritance goals. I do have a reservation for how this would affect backwards compatibility.)

I dunno if Ruby's current behavior is intentional (having purposeful benefits) but I find it annoying as it makes it difficult to override a behavior in third-party libraries. When I own the classes, I can simply refactor them to prefer mixins as to avoid this issue, but this doesn't solve the dilemma when dealing with vendor libraries.

--
http://bugs.ruby-lang.org/

Updated by mlanza (Mario Lanza) over 8 years ago

Thanks for the feedback and the suggestions.
Will be watching for the arrival of Module#prepend. :)

Updated by marcandre (Marc-Andre Lafortune) over 8 years ago

  • Status changed from Open to Closed

Closed, as duplicate of Module#prepend request.

Also available in: Atom PDF