Why can't classes be used as modules?












4















Module is the superclass of Class:



Class.superclass
# => Module


In OOP, this implies that an instance of Class can be used in every place where an instance of Module can be used.



Surprisingly, this is not the case with Class instances in Ruby:



class C end
c = C.new

module M end
# Let's do all the extend/include/prepend stuff with M!
c.extend M
C.include M
C.prepend M

# All worked fine until this line.
# Let's turn to classes now!

# First, get a class to work with.
class C_as_M end
C_as_M.class.superclass
# => Module # yes, C_as_M is an instance of a Module child
C_as_M.is_a? Module
# => true # yes, it is still a Module

# And now let's do the same extend/include/prepend stuff with C_as_M!

c.extend C_as_M
# => TypeError: wrong argument type Class (expected Module)
C.include C_as_M
# => TypeError: wrong argument type Class (expected Module)
C.prepend C_as_M
# => TypeError: wrong argument type Class (expected Module)


What is the reason for the violation of this OOP principle? Why can't classes be used as modules?










share|improve this question

























  • But def MyClass end, MyClass.superclass returns #=> Object.

    – iGian
    Nov 23 '18 at 16:27











  • @iGian It's not the same. You're asking the wrong object. You have to ask MyClass.class.superclass. Compare to: Integer.superclass # => Numeric You're asking like 5.superclass

    – Min-Soo Pipefeet
    Nov 23 '18 at 16:36











  • @iGian: Your code doesn't return Object, it raises a NoMethodError exception since MyClass evaluates to nil and nil doesn't have a superclass method.

    – Jörg W Mittag
    Nov 24 '18 at 9:06











  • @JörgWMittag, I actually made a typo, it is class MyClass end; p MyClass.superclass #=> Object, not def .

    – iGian
    Nov 24 '18 at 15:25






  • 3





    This is a great question!

    – Cary Swoveland
    Nov 24 '18 at 18:53
















4















Module is the superclass of Class:



Class.superclass
# => Module


In OOP, this implies that an instance of Class can be used in every place where an instance of Module can be used.



Surprisingly, this is not the case with Class instances in Ruby:



class C end
c = C.new

module M end
# Let's do all the extend/include/prepend stuff with M!
c.extend M
C.include M
C.prepend M

# All worked fine until this line.
# Let's turn to classes now!

# First, get a class to work with.
class C_as_M end
C_as_M.class.superclass
# => Module # yes, C_as_M is an instance of a Module child
C_as_M.is_a? Module
# => true # yes, it is still a Module

# And now let's do the same extend/include/prepend stuff with C_as_M!

c.extend C_as_M
# => TypeError: wrong argument type Class (expected Module)
C.include C_as_M
# => TypeError: wrong argument type Class (expected Module)
C.prepend C_as_M
# => TypeError: wrong argument type Class (expected Module)


What is the reason for the violation of this OOP principle? Why can't classes be used as modules?










share|improve this question

























  • But def MyClass end, MyClass.superclass returns #=> Object.

    – iGian
    Nov 23 '18 at 16:27











  • @iGian It's not the same. You're asking the wrong object. You have to ask MyClass.class.superclass. Compare to: Integer.superclass # => Numeric You're asking like 5.superclass

    – Min-Soo Pipefeet
    Nov 23 '18 at 16:36











  • @iGian: Your code doesn't return Object, it raises a NoMethodError exception since MyClass evaluates to nil and nil doesn't have a superclass method.

    – Jörg W Mittag
    Nov 24 '18 at 9:06











  • @JörgWMittag, I actually made a typo, it is class MyClass end; p MyClass.superclass #=> Object, not def .

    – iGian
    Nov 24 '18 at 15:25






  • 3





    This is a great question!

    – Cary Swoveland
    Nov 24 '18 at 18:53














4












4








4


1






Module is the superclass of Class:



Class.superclass
# => Module


In OOP, this implies that an instance of Class can be used in every place where an instance of Module can be used.



Surprisingly, this is not the case with Class instances in Ruby:



class C end
c = C.new

module M end
# Let's do all the extend/include/prepend stuff with M!
c.extend M
C.include M
C.prepend M

# All worked fine until this line.
# Let's turn to classes now!

# First, get a class to work with.
class C_as_M end
C_as_M.class.superclass
# => Module # yes, C_as_M is an instance of a Module child
C_as_M.is_a? Module
# => true # yes, it is still a Module

# And now let's do the same extend/include/prepend stuff with C_as_M!

c.extend C_as_M
# => TypeError: wrong argument type Class (expected Module)
C.include C_as_M
# => TypeError: wrong argument type Class (expected Module)
C.prepend C_as_M
# => TypeError: wrong argument type Class (expected Module)


What is the reason for the violation of this OOP principle? Why can't classes be used as modules?










share|improve this question
















Module is the superclass of Class:



Class.superclass
# => Module


In OOP, this implies that an instance of Class can be used in every place where an instance of Module can be used.



Surprisingly, this is not the case with Class instances in Ruby:



class C end
c = C.new

module M end
# Let's do all the extend/include/prepend stuff with M!
c.extend M
C.include M
C.prepend M

# All worked fine until this line.
# Let's turn to classes now!

# First, get a class to work with.
class C_as_M end
C_as_M.class.superclass
# => Module # yes, C_as_M is an instance of a Module child
C_as_M.is_a? Module
# => true # yes, it is still a Module

# And now let's do the same extend/include/prepend stuff with C_as_M!

c.extend C_as_M
# => TypeError: wrong argument type Class (expected Module)
C.include C_as_M
# => TypeError: wrong argument type Class (expected Module)
C.prepend C_as_M
# => TypeError: wrong argument type Class (expected Module)


What is the reason for the violation of this OOP principle? Why can't classes be used as modules?







ruby class module superclass






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 26 '18 at 7:03









sawa

131k29205303




131k29205303










asked Nov 23 '18 at 15:57









Min-Soo PipefeetMin-Soo Pipefeet

19510




19510













  • But def MyClass end, MyClass.superclass returns #=> Object.

    – iGian
    Nov 23 '18 at 16:27











  • @iGian It's not the same. You're asking the wrong object. You have to ask MyClass.class.superclass. Compare to: Integer.superclass # => Numeric You're asking like 5.superclass

    – Min-Soo Pipefeet
    Nov 23 '18 at 16:36











  • @iGian: Your code doesn't return Object, it raises a NoMethodError exception since MyClass evaluates to nil and nil doesn't have a superclass method.

    – Jörg W Mittag
    Nov 24 '18 at 9:06











  • @JörgWMittag, I actually made a typo, it is class MyClass end; p MyClass.superclass #=> Object, not def .

    – iGian
    Nov 24 '18 at 15:25






  • 3





    This is a great question!

    – Cary Swoveland
    Nov 24 '18 at 18:53



















  • But def MyClass end, MyClass.superclass returns #=> Object.

    – iGian
    Nov 23 '18 at 16:27











  • @iGian It's not the same. You're asking the wrong object. You have to ask MyClass.class.superclass. Compare to: Integer.superclass # => Numeric You're asking like 5.superclass

    – Min-Soo Pipefeet
    Nov 23 '18 at 16:36











  • @iGian: Your code doesn't return Object, it raises a NoMethodError exception since MyClass evaluates to nil and nil doesn't have a superclass method.

    – Jörg W Mittag
    Nov 24 '18 at 9:06











  • @JörgWMittag, I actually made a typo, it is class MyClass end; p MyClass.superclass #=> Object, not def .

    – iGian
    Nov 24 '18 at 15:25






  • 3





    This is a great question!

    – Cary Swoveland
    Nov 24 '18 at 18:53

















But def MyClass end, MyClass.superclass returns #=> Object.

– iGian
Nov 23 '18 at 16:27





But def MyClass end, MyClass.superclass returns #=> Object.

– iGian
Nov 23 '18 at 16:27













@iGian It's not the same. You're asking the wrong object. You have to ask MyClass.class.superclass. Compare to: Integer.superclass # => Numeric You're asking like 5.superclass

– Min-Soo Pipefeet
Nov 23 '18 at 16:36





@iGian It's not the same. You're asking the wrong object. You have to ask MyClass.class.superclass. Compare to: Integer.superclass # => Numeric You're asking like 5.superclass

– Min-Soo Pipefeet
Nov 23 '18 at 16:36













@iGian: Your code doesn't return Object, it raises a NoMethodError exception since MyClass evaluates to nil and nil doesn't have a superclass method.

– Jörg W Mittag
Nov 24 '18 at 9:06





@iGian: Your code doesn't return Object, it raises a NoMethodError exception since MyClass evaluates to nil and nil doesn't have a superclass method.

– Jörg W Mittag
Nov 24 '18 at 9:06













@JörgWMittag, I actually made a typo, it is class MyClass end; p MyClass.superclass #=> Object, not def .

– iGian
Nov 24 '18 at 15:25





@JörgWMittag, I actually made a typo, it is class MyClass end; p MyClass.superclass #=> Object, not def .

– iGian
Nov 24 '18 at 15:25




3




3





This is a great question!

– Cary Swoveland
Nov 24 '18 at 18:53





This is a great question!

– Cary Swoveland
Nov 24 '18 at 18:53












2 Answers
2






active

oldest

votes


















2















In OOP, this implies that an instance of Class can be used in every place where an instance of Module can be used.




You are confusing subtypes and subclasses, i.e. subtyping (which is about refinement of contracts) and inheritance (which is about differential code re-use).



In Ruby, inheritance creates a subclass, but not a subtype. (In fact, "type" is a concept that exists only in the programmer's head in Ruby.)



Class < Module is one example of a subclass that is not a subtype, and StringIO <: IO is an example of a subtype that is not a subclass.




What is the reason for the violation of this OOP principle?




It's not an OO principle. Subtyping and OO are completely orthogonal. There are OO languages without subtyping as well as non-OO languages with subtyping.





Note: it would actually be easy to collapse modules and classes into one. They could inherit from each other both the same way as classes inherit from classes as well as the way classes inherit from modules.






share|improve this answer



















  • 2





    And still the question: why classes can't be used as modules? I mean: what is the internal reason in Ruby not to let classes include/prepend other classes?

    – Min-Soo Pipefeet
    Nov 23 '18 at 19:26











  • Regarding the other topics: of course, one may argue that in Ruby classes weren't types and inheritance does not create subtypes, as by the authors of the book Programming Ruby 1.9 & 2.0. But the fact is (as pointed out by the same authors): "Inheritance allows you to create a class that is a refinement or specialization of another class. ... The basic mechanism of subclassing ... The child inherits all of the capabilities of its parent class - all the parent's instance methods are available in instances of the child." In other words: inheritance does create a subtype (OO or not).

    – Min-Soo Pipefeet
    Nov 24 '18 at 8:35











  • "And still the question: why classes can't be used as modules?" – That question is off-topic for Stack Overflow, since it can only be answered by Yukihiro Matsumoto himself; only he knows why he made that choice. If I had to make a guess, it would be too confusing to have two different kinds of inheritance involving the same language construct. The Self language has shown that you don't even need classes, you can collapse classes and objects into one construct (only objects), and Beta and gBeta have shown that you can unify objects, classes, and methods into one construct (patterns).

    – Jörg W Mittag
    Nov 24 '18 at 9:12











  • And yet, languages in which objects, classes, methods, and fields are four distinct things (with interfaces and mixins optionally added on top, plus another mechanism for large-scale structuring such as modules or packages) seem to dominate, because abstracting and unifying too much seems to confuse programmers. Even people who were involved with Beta admit that that it was hard to structure programs correctly when there was no distinction between methods and classes.

    – Jörg W Mittag
    Nov 24 '18 at 9:14






  • 1





    "..but you found out that that is not the case, that you can't use a class everywhere you can use a module" - Right, but that's not because of the inheritance. It's because of Ruby's deliberate distortion of the inheritance in this special case. Hence, the question for the rationale behind this distortion. (Btw: the question is absolutely in-topic, and Yukihiro Matsumoto is highly welcome to be a user of Stack Overflow, as well as other creators and maintainers of Ruby.)

    – Min-Soo Pipefeet
    Nov 24 '18 at 9:28



















1














I guess, meanwhile I've figured out why classes can't be used as modules in Ruby.

Or to be more specific: why classes can't be included/prepended.



It's the same reason as why Ruby does not support multiple inheritance:
to avoid ambiguity/complexity in the ancestors hierarchy.



After a short explanation of how multiple inheritance affects the ancestors hierarchy, I will explain why including/prepending classes would introduce multiple inheritance or/and similar complexities through the back door.





With single inheritance, the ancestors hierarchy of a given class is simply a chain.

The chain may be long or short, but it's always just a linear chain of ancestor classes:



File.ancestors
=> [File, IO, File::Constants, Enumerable, Object, Kernel, BasicObject]
Object.ancestors
=> [Object, Kernel, BasicObject]
BasicObject.ancestors
=> [BasicObject]


So looking up an instance variable or method is simple as that: look in the current class; if not found, go to the next ancestor and look there; if not found, go to the next ancestor and look there...





With multiple inheritance, the ancestors hierarchy can branch.

Hypothetically given



class A end
class B < A; end
class C < A; end

class D < B, C
end


produces the following ancestors graph of class D:



enter image description here



This increases complexity and ambiguity, it induces "the diamond problem":




  • Do instances of D share instance variables in class A inherited via B and inherited via C?

  • If they do not share them,

    we need an extended syntax to designate

    whether we want to access an instance variable in A via B or via C.


Ruby is designed to avoid this complexity. Hence, no multiple inheritance by design.





Including/prepending modules

is another mean of constructing/manipulating the ancestors hierarchy:



class MyBase < BasicObject; end
class C < MyBase; end
C.ancestors
=> [C, MyBase, BasicObject]

module IncludeMe end
C.include IncludeMe
C.ancestors
=> [C, IncludeMe, MyBase, BasicObject]

module PrependMe end
C.prepend PrependMe
C.ancestors
=> [PrependMe, C, IncludeMe, MyBase, BasicObject]

module Intermediate end
MyBase.include Intermediate
C.ancestors
=> [PrependMe, C, IncludeMe, MyBase, Intermediate, BasicObject]


Including/prepending modules only keeps the ancestors chain a simple chain.

No bad things happen.





Including/prepending classes



Now imagine IncludeMe, PrependMe, and Intermediate were not modules but classes.



To keep it simple, I will stick to one class only:



class PrependMe
def to_s
"Hello from prepended #{super}!"
end
end


Keep in mind that PrependMe inherits from Object by default:



PrependMe.ancestors
# => [PrependMe, Object, Kernel, BasicObject]


Also, pay attention to that fact that in Ruby it's not possible to create baseless classes (BasicObject is the only baseless class):



class BaselessClass < nil # Try it!
end # You'll fail.
# => TypeError: superclass must be a Class (NilClass given)


So, each class X (except BasicObject) has an ancestors chain with at least two parts,
always ending with BasicObject:



X.ancestors
# => [X, ..., BasicObject]
# the shortest possible chain is [X, BasicObject]
# the usual chain is [X, ..., Object, Kernel, BasicObject]


So, what should the ancestors hierarchy of class C look like after C.prepend PrependMe?



C.ancestors
=> [PrependMe, Object, Kernel, C, MyBase, BasicObject] ?
=> [PrependMe, C, Object, Kernel, MyBase, BasicObject] ?
=> [PrependMe, C, MyBase, Object, Kernel, BasicObject] ?
=> [PrependMe, C, MyBase, BasicObject] ? # Object and Kernel omitted on purpose


Or should the ancestors hierarchy even branch at PrependMe into an own branch for Object? With all the effects of the diamond problem.



One could reasonably argue for each of these options.

The answer depends on what you want to see as the result of (c = C.new).to_s.



Obviously, including/prepending classes would introduce ambiguity and complexity similar to or even worse than these of multiple inheritance. And as in the case of multiple inheritance, it's a deliberate decision of Ruby to avoid this.



Apparently, this is the reason why including/appending classes is forbidden in Ruby.






share|improve this answer


























  • You are assuming that including classes would work differently than including modules. If including classes worked the same way as including modules, the same linearization property would apply as for modules, and there would be no diamonds. Therefore, it would be perfectly possible to include classes the same way as modules. The problem with both this approach and yours, is that then the same language construct (classes) could be used for two different kinds of inheritance (classical class-based inheritance and linearized mixin inheritance) that work differently.

    – Jörg W Mittag
    Nov 24 '18 at 18:31











  • I guess (but I do not know) that this might be the reason to separate the two; to alleviate this confusion. (Although note that modules also serve a secondary purpose as namespaces, which interestingly is a trait that classes actually inherit from modules.) So, by my argument, there could even be three language constructs: namespaces, mixins, and classes.

    – Jörg W Mittag
    Nov 24 '18 at 18:33











  • Mixin inheritance is not multiple inheritance, it is, in fact, the exact dual: in multiple inheritance, a class appears once in the inheritance graph, and may have multiple superclasses, whereas in mixin inheritance, a class appears multiple times in the inheritance graph and each time has only one superclass.

    – Jörg W Mittag
    Nov 24 '18 at 18:35











  • The way mixins work in Ruby is as follows: Step 1: a new class M' is created whose method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer point to the same place in memory as the method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer of the module M. Step 2: the current superclass of C is made the superclass of M'. Step 3: M' is made the superclass of C. You could perform exactly those same steps regardless of whether M is a module or class, with exactly the …

    – Jörg W Mittag
    Nov 24 '18 at 19:15











  • … same results. Since M is copied to a class anyway in Step 1, it really doesn't matter at all for the algorithm whether M is a class or module, it will work exactly the same. The only thing classes have that modules don't, is a superclass pointer, but that is not used for M and overwritten for M', so it makes no difference.

    – Jörg W Mittag
    Nov 24 '18 at 19:16













Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53449705%2fwhy-cant-classes-be-used-as-modules%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









2















In OOP, this implies that an instance of Class can be used in every place where an instance of Module can be used.




You are confusing subtypes and subclasses, i.e. subtyping (which is about refinement of contracts) and inheritance (which is about differential code re-use).



In Ruby, inheritance creates a subclass, but not a subtype. (In fact, "type" is a concept that exists only in the programmer's head in Ruby.)



Class < Module is one example of a subclass that is not a subtype, and StringIO <: IO is an example of a subtype that is not a subclass.




What is the reason for the violation of this OOP principle?




It's not an OO principle. Subtyping and OO are completely orthogonal. There are OO languages without subtyping as well as non-OO languages with subtyping.





Note: it would actually be easy to collapse modules and classes into one. They could inherit from each other both the same way as classes inherit from classes as well as the way classes inherit from modules.






share|improve this answer



















  • 2





    And still the question: why classes can't be used as modules? I mean: what is the internal reason in Ruby not to let classes include/prepend other classes?

    – Min-Soo Pipefeet
    Nov 23 '18 at 19:26











  • Regarding the other topics: of course, one may argue that in Ruby classes weren't types and inheritance does not create subtypes, as by the authors of the book Programming Ruby 1.9 & 2.0. But the fact is (as pointed out by the same authors): "Inheritance allows you to create a class that is a refinement or specialization of another class. ... The basic mechanism of subclassing ... The child inherits all of the capabilities of its parent class - all the parent's instance methods are available in instances of the child." In other words: inheritance does create a subtype (OO or not).

    – Min-Soo Pipefeet
    Nov 24 '18 at 8:35











  • "And still the question: why classes can't be used as modules?" – That question is off-topic for Stack Overflow, since it can only be answered by Yukihiro Matsumoto himself; only he knows why he made that choice. If I had to make a guess, it would be too confusing to have two different kinds of inheritance involving the same language construct. The Self language has shown that you don't even need classes, you can collapse classes and objects into one construct (only objects), and Beta and gBeta have shown that you can unify objects, classes, and methods into one construct (patterns).

    – Jörg W Mittag
    Nov 24 '18 at 9:12











  • And yet, languages in which objects, classes, methods, and fields are four distinct things (with interfaces and mixins optionally added on top, plus another mechanism for large-scale structuring such as modules or packages) seem to dominate, because abstracting and unifying too much seems to confuse programmers. Even people who were involved with Beta admit that that it was hard to structure programs correctly when there was no distinction between methods and classes.

    – Jörg W Mittag
    Nov 24 '18 at 9:14






  • 1





    "..but you found out that that is not the case, that you can't use a class everywhere you can use a module" - Right, but that's not because of the inheritance. It's because of Ruby's deliberate distortion of the inheritance in this special case. Hence, the question for the rationale behind this distortion. (Btw: the question is absolutely in-topic, and Yukihiro Matsumoto is highly welcome to be a user of Stack Overflow, as well as other creators and maintainers of Ruby.)

    – Min-Soo Pipefeet
    Nov 24 '18 at 9:28
















2















In OOP, this implies that an instance of Class can be used in every place where an instance of Module can be used.




You are confusing subtypes and subclasses, i.e. subtyping (which is about refinement of contracts) and inheritance (which is about differential code re-use).



In Ruby, inheritance creates a subclass, but not a subtype. (In fact, "type" is a concept that exists only in the programmer's head in Ruby.)



Class < Module is one example of a subclass that is not a subtype, and StringIO <: IO is an example of a subtype that is not a subclass.




What is the reason for the violation of this OOP principle?




It's not an OO principle. Subtyping and OO are completely orthogonal. There are OO languages without subtyping as well as non-OO languages with subtyping.





Note: it would actually be easy to collapse modules and classes into one. They could inherit from each other both the same way as classes inherit from classes as well as the way classes inherit from modules.






share|improve this answer



















  • 2





    And still the question: why classes can't be used as modules? I mean: what is the internal reason in Ruby not to let classes include/prepend other classes?

    – Min-Soo Pipefeet
    Nov 23 '18 at 19:26











  • Regarding the other topics: of course, one may argue that in Ruby classes weren't types and inheritance does not create subtypes, as by the authors of the book Programming Ruby 1.9 & 2.0. But the fact is (as pointed out by the same authors): "Inheritance allows you to create a class that is a refinement or specialization of another class. ... The basic mechanism of subclassing ... The child inherits all of the capabilities of its parent class - all the parent's instance methods are available in instances of the child." In other words: inheritance does create a subtype (OO or not).

    – Min-Soo Pipefeet
    Nov 24 '18 at 8:35











  • "And still the question: why classes can't be used as modules?" – That question is off-topic for Stack Overflow, since it can only be answered by Yukihiro Matsumoto himself; only he knows why he made that choice. If I had to make a guess, it would be too confusing to have two different kinds of inheritance involving the same language construct. The Self language has shown that you don't even need classes, you can collapse classes and objects into one construct (only objects), and Beta and gBeta have shown that you can unify objects, classes, and methods into one construct (patterns).

    – Jörg W Mittag
    Nov 24 '18 at 9:12











  • And yet, languages in which objects, classes, methods, and fields are four distinct things (with interfaces and mixins optionally added on top, plus another mechanism for large-scale structuring such as modules or packages) seem to dominate, because abstracting and unifying too much seems to confuse programmers. Even people who were involved with Beta admit that that it was hard to structure programs correctly when there was no distinction between methods and classes.

    – Jörg W Mittag
    Nov 24 '18 at 9:14






  • 1





    "..but you found out that that is not the case, that you can't use a class everywhere you can use a module" - Right, but that's not because of the inheritance. It's because of Ruby's deliberate distortion of the inheritance in this special case. Hence, the question for the rationale behind this distortion. (Btw: the question is absolutely in-topic, and Yukihiro Matsumoto is highly welcome to be a user of Stack Overflow, as well as other creators and maintainers of Ruby.)

    – Min-Soo Pipefeet
    Nov 24 '18 at 9:28














2












2








2








In OOP, this implies that an instance of Class can be used in every place where an instance of Module can be used.




You are confusing subtypes and subclasses, i.e. subtyping (which is about refinement of contracts) and inheritance (which is about differential code re-use).



In Ruby, inheritance creates a subclass, but not a subtype. (In fact, "type" is a concept that exists only in the programmer's head in Ruby.)



Class < Module is one example of a subclass that is not a subtype, and StringIO <: IO is an example of a subtype that is not a subclass.




What is the reason for the violation of this OOP principle?




It's not an OO principle. Subtyping and OO are completely orthogonal. There are OO languages without subtyping as well as non-OO languages with subtyping.





Note: it would actually be easy to collapse modules and classes into one. They could inherit from each other both the same way as classes inherit from classes as well as the way classes inherit from modules.






share|improve this answer














In OOP, this implies that an instance of Class can be used in every place where an instance of Module can be used.




You are confusing subtypes and subclasses, i.e. subtyping (which is about refinement of contracts) and inheritance (which is about differential code re-use).



In Ruby, inheritance creates a subclass, but not a subtype. (In fact, "type" is a concept that exists only in the programmer's head in Ruby.)



Class < Module is one example of a subclass that is not a subtype, and StringIO <: IO is an example of a subtype that is not a subclass.




What is the reason for the violation of this OOP principle?




It's not an OO principle. Subtyping and OO are completely orthogonal. There are OO languages without subtyping as well as non-OO languages with subtyping.





Note: it would actually be easy to collapse modules and classes into one. They could inherit from each other both the same way as classes inherit from classes as well as the way classes inherit from modules.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 23 '18 at 19:11









Jörg W MittagJörg W Mittag

291k63356550




291k63356550








  • 2





    And still the question: why classes can't be used as modules? I mean: what is the internal reason in Ruby not to let classes include/prepend other classes?

    – Min-Soo Pipefeet
    Nov 23 '18 at 19:26











  • Regarding the other topics: of course, one may argue that in Ruby classes weren't types and inheritance does not create subtypes, as by the authors of the book Programming Ruby 1.9 & 2.0. But the fact is (as pointed out by the same authors): "Inheritance allows you to create a class that is a refinement or specialization of another class. ... The basic mechanism of subclassing ... The child inherits all of the capabilities of its parent class - all the parent's instance methods are available in instances of the child." In other words: inheritance does create a subtype (OO or not).

    – Min-Soo Pipefeet
    Nov 24 '18 at 8:35











  • "And still the question: why classes can't be used as modules?" – That question is off-topic for Stack Overflow, since it can only be answered by Yukihiro Matsumoto himself; only he knows why he made that choice. If I had to make a guess, it would be too confusing to have two different kinds of inheritance involving the same language construct. The Self language has shown that you don't even need classes, you can collapse classes and objects into one construct (only objects), and Beta and gBeta have shown that you can unify objects, classes, and methods into one construct (patterns).

    – Jörg W Mittag
    Nov 24 '18 at 9:12











  • And yet, languages in which objects, classes, methods, and fields are four distinct things (with interfaces and mixins optionally added on top, plus another mechanism for large-scale structuring such as modules or packages) seem to dominate, because abstracting and unifying too much seems to confuse programmers. Even people who were involved with Beta admit that that it was hard to structure programs correctly when there was no distinction between methods and classes.

    – Jörg W Mittag
    Nov 24 '18 at 9:14






  • 1





    "..but you found out that that is not the case, that you can't use a class everywhere you can use a module" - Right, but that's not because of the inheritance. It's because of Ruby's deliberate distortion of the inheritance in this special case. Hence, the question for the rationale behind this distortion. (Btw: the question is absolutely in-topic, and Yukihiro Matsumoto is highly welcome to be a user of Stack Overflow, as well as other creators and maintainers of Ruby.)

    – Min-Soo Pipefeet
    Nov 24 '18 at 9:28














  • 2





    And still the question: why classes can't be used as modules? I mean: what is the internal reason in Ruby not to let classes include/prepend other classes?

    – Min-Soo Pipefeet
    Nov 23 '18 at 19:26











  • Regarding the other topics: of course, one may argue that in Ruby classes weren't types and inheritance does not create subtypes, as by the authors of the book Programming Ruby 1.9 & 2.0. But the fact is (as pointed out by the same authors): "Inheritance allows you to create a class that is a refinement or specialization of another class. ... The basic mechanism of subclassing ... The child inherits all of the capabilities of its parent class - all the parent's instance methods are available in instances of the child." In other words: inheritance does create a subtype (OO or not).

    – Min-Soo Pipefeet
    Nov 24 '18 at 8:35











  • "And still the question: why classes can't be used as modules?" – That question is off-topic for Stack Overflow, since it can only be answered by Yukihiro Matsumoto himself; only he knows why he made that choice. If I had to make a guess, it would be too confusing to have two different kinds of inheritance involving the same language construct. The Self language has shown that you don't even need classes, you can collapse classes and objects into one construct (only objects), and Beta and gBeta have shown that you can unify objects, classes, and methods into one construct (patterns).

    – Jörg W Mittag
    Nov 24 '18 at 9:12











  • And yet, languages in which objects, classes, methods, and fields are four distinct things (with interfaces and mixins optionally added on top, plus another mechanism for large-scale structuring such as modules or packages) seem to dominate, because abstracting and unifying too much seems to confuse programmers. Even people who were involved with Beta admit that that it was hard to structure programs correctly when there was no distinction between methods and classes.

    – Jörg W Mittag
    Nov 24 '18 at 9:14






  • 1





    "..but you found out that that is not the case, that you can't use a class everywhere you can use a module" - Right, but that's not because of the inheritance. It's because of Ruby's deliberate distortion of the inheritance in this special case. Hence, the question for the rationale behind this distortion. (Btw: the question is absolutely in-topic, and Yukihiro Matsumoto is highly welcome to be a user of Stack Overflow, as well as other creators and maintainers of Ruby.)

    – Min-Soo Pipefeet
    Nov 24 '18 at 9:28








2




2





And still the question: why classes can't be used as modules? I mean: what is the internal reason in Ruby not to let classes include/prepend other classes?

– Min-Soo Pipefeet
Nov 23 '18 at 19:26





And still the question: why classes can't be used as modules? I mean: what is the internal reason in Ruby not to let classes include/prepend other classes?

– Min-Soo Pipefeet
Nov 23 '18 at 19:26













Regarding the other topics: of course, one may argue that in Ruby classes weren't types and inheritance does not create subtypes, as by the authors of the book Programming Ruby 1.9 & 2.0. But the fact is (as pointed out by the same authors): "Inheritance allows you to create a class that is a refinement or specialization of another class. ... The basic mechanism of subclassing ... The child inherits all of the capabilities of its parent class - all the parent's instance methods are available in instances of the child." In other words: inheritance does create a subtype (OO or not).

– Min-Soo Pipefeet
Nov 24 '18 at 8:35





Regarding the other topics: of course, one may argue that in Ruby classes weren't types and inheritance does not create subtypes, as by the authors of the book Programming Ruby 1.9 & 2.0. But the fact is (as pointed out by the same authors): "Inheritance allows you to create a class that is a refinement or specialization of another class. ... The basic mechanism of subclassing ... The child inherits all of the capabilities of its parent class - all the parent's instance methods are available in instances of the child." In other words: inheritance does create a subtype (OO or not).

– Min-Soo Pipefeet
Nov 24 '18 at 8:35













"And still the question: why classes can't be used as modules?" – That question is off-topic for Stack Overflow, since it can only be answered by Yukihiro Matsumoto himself; only he knows why he made that choice. If I had to make a guess, it would be too confusing to have two different kinds of inheritance involving the same language construct. The Self language has shown that you don't even need classes, you can collapse classes and objects into one construct (only objects), and Beta and gBeta have shown that you can unify objects, classes, and methods into one construct (patterns).

– Jörg W Mittag
Nov 24 '18 at 9:12





"And still the question: why classes can't be used as modules?" – That question is off-topic for Stack Overflow, since it can only be answered by Yukihiro Matsumoto himself; only he knows why he made that choice. If I had to make a guess, it would be too confusing to have two different kinds of inheritance involving the same language construct. The Self language has shown that you don't even need classes, you can collapse classes and objects into one construct (only objects), and Beta and gBeta have shown that you can unify objects, classes, and methods into one construct (patterns).

– Jörg W Mittag
Nov 24 '18 at 9:12













And yet, languages in which objects, classes, methods, and fields are four distinct things (with interfaces and mixins optionally added on top, plus another mechanism for large-scale structuring such as modules or packages) seem to dominate, because abstracting and unifying too much seems to confuse programmers. Even people who were involved with Beta admit that that it was hard to structure programs correctly when there was no distinction between methods and classes.

– Jörg W Mittag
Nov 24 '18 at 9:14





And yet, languages in which objects, classes, methods, and fields are four distinct things (with interfaces and mixins optionally added on top, plus another mechanism for large-scale structuring such as modules or packages) seem to dominate, because abstracting and unifying too much seems to confuse programmers. Even people who were involved with Beta admit that that it was hard to structure programs correctly when there was no distinction between methods and classes.

– Jörg W Mittag
Nov 24 '18 at 9:14




1




1





"..but you found out that that is not the case, that you can't use a class everywhere you can use a module" - Right, but that's not because of the inheritance. It's because of Ruby's deliberate distortion of the inheritance in this special case. Hence, the question for the rationale behind this distortion. (Btw: the question is absolutely in-topic, and Yukihiro Matsumoto is highly welcome to be a user of Stack Overflow, as well as other creators and maintainers of Ruby.)

– Min-Soo Pipefeet
Nov 24 '18 at 9:28





"..but you found out that that is not the case, that you can't use a class everywhere you can use a module" - Right, but that's not because of the inheritance. It's because of Ruby's deliberate distortion of the inheritance in this special case. Hence, the question for the rationale behind this distortion. (Btw: the question is absolutely in-topic, and Yukihiro Matsumoto is highly welcome to be a user of Stack Overflow, as well as other creators and maintainers of Ruby.)

– Min-Soo Pipefeet
Nov 24 '18 at 9:28













1














I guess, meanwhile I've figured out why classes can't be used as modules in Ruby.

Or to be more specific: why classes can't be included/prepended.



It's the same reason as why Ruby does not support multiple inheritance:
to avoid ambiguity/complexity in the ancestors hierarchy.



After a short explanation of how multiple inheritance affects the ancestors hierarchy, I will explain why including/prepending classes would introduce multiple inheritance or/and similar complexities through the back door.





With single inheritance, the ancestors hierarchy of a given class is simply a chain.

The chain may be long or short, but it's always just a linear chain of ancestor classes:



File.ancestors
=> [File, IO, File::Constants, Enumerable, Object, Kernel, BasicObject]
Object.ancestors
=> [Object, Kernel, BasicObject]
BasicObject.ancestors
=> [BasicObject]


So looking up an instance variable or method is simple as that: look in the current class; if not found, go to the next ancestor and look there; if not found, go to the next ancestor and look there...





With multiple inheritance, the ancestors hierarchy can branch.

Hypothetically given



class A end
class B < A; end
class C < A; end

class D < B, C
end


produces the following ancestors graph of class D:



enter image description here



This increases complexity and ambiguity, it induces "the diamond problem":




  • Do instances of D share instance variables in class A inherited via B and inherited via C?

  • If they do not share them,

    we need an extended syntax to designate

    whether we want to access an instance variable in A via B or via C.


Ruby is designed to avoid this complexity. Hence, no multiple inheritance by design.





Including/prepending modules

is another mean of constructing/manipulating the ancestors hierarchy:



class MyBase < BasicObject; end
class C < MyBase; end
C.ancestors
=> [C, MyBase, BasicObject]

module IncludeMe end
C.include IncludeMe
C.ancestors
=> [C, IncludeMe, MyBase, BasicObject]

module PrependMe end
C.prepend PrependMe
C.ancestors
=> [PrependMe, C, IncludeMe, MyBase, BasicObject]

module Intermediate end
MyBase.include Intermediate
C.ancestors
=> [PrependMe, C, IncludeMe, MyBase, Intermediate, BasicObject]


Including/prepending modules only keeps the ancestors chain a simple chain.

No bad things happen.





Including/prepending classes



Now imagine IncludeMe, PrependMe, and Intermediate were not modules but classes.



To keep it simple, I will stick to one class only:



class PrependMe
def to_s
"Hello from prepended #{super}!"
end
end


Keep in mind that PrependMe inherits from Object by default:



PrependMe.ancestors
# => [PrependMe, Object, Kernel, BasicObject]


Also, pay attention to that fact that in Ruby it's not possible to create baseless classes (BasicObject is the only baseless class):



class BaselessClass < nil # Try it!
end # You'll fail.
# => TypeError: superclass must be a Class (NilClass given)


So, each class X (except BasicObject) has an ancestors chain with at least two parts,
always ending with BasicObject:



X.ancestors
# => [X, ..., BasicObject]
# the shortest possible chain is [X, BasicObject]
# the usual chain is [X, ..., Object, Kernel, BasicObject]


So, what should the ancestors hierarchy of class C look like after C.prepend PrependMe?



C.ancestors
=> [PrependMe, Object, Kernel, C, MyBase, BasicObject] ?
=> [PrependMe, C, Object, Kernel, MyBase, BasicObject] ?
=> [PrependMe, C, MyBase, Object, Kernel, BasicObject] ?
=> [PrependMe, C, MyBase, BasicObject] ? # Object and Kernel omitted on purpose


Or should the ancestors hierarchy even branch at PrependMe into an own branch for Object? With all the effects of the diamond problem.



One could reasonably argue for each of these options.

The answer depends on what you want to see as the result of (c = C.new).to_s.



Obviously, including/prepending classes would introduce ambiguity and complexity similar to or even worse than these of multiple inheritance. And as in the case of multiple inheritance, it's a deliberate decision of Ruby to avoid this.



Apparently, this is the reason why including/appending classes is forbidden in Ruby.






share|improve this answer


























  • You are assuming that including classes would work differently than including modules. If including classes worked the same way as including modules, the same linearization property would apply as for modules, and there would be no diamonds. Therefore, it would be perfectly possible to include classes the same way as modules. The problem with both this approach and yours, is that then the same language construct (classes) could be used for two different kinds of inheritance (classical class-based inheritance and linearized mixin inheritance) that work differently.

    – Jörg W Mittag
    Nov 24 '18 at 18:31











  • I guess (but I do not know) that this might be the reason to separate the two; to alleviate this confusion. (Although note that modules also serve a secondary purpose as namespaces, which interestingly is a trait that classes actually inherit from modules.) So, by my argument, there could even be three language constructs: namespaces, mixins, and classes.

    – Jörg W Mittag
    Nov 24 '18 at 18:33











  • Mixin inheritance is not multiple inheritance, it is, in fact, the exact dual: in multiple inheritance, a class appears once in the inheritance graph, and may have multiple superclasses, whereas in mixin inheritance, a class appears multiple times in the inheritance graph and each time has only one superclass.

    – Jörg W Mittag
    Nov 24 '18 at 18:35











  • The way mixins work in Ruby is as follows: Step 1: a new class M' is created whose method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer point to the same place in memory as the method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer of the module M. Step 2: the current superclass of C is made the superclass of M'. Step 3: M' is made the superclass of C. You could perform exactly those same steps regardless of whether M is a module or class, with exactly the …

    – Jörg W Mittag
    Nov 24 '18 at 19:15











  • … same results. Since M is copied to a class anyway in Step 1, it really doesn't matter at all for the algorithm whether M is a class or module, it will work exactly the same. The only thing classes have that modules don't, is a superclass pointer, but that is not used for M and overwritten for M', so it makes no difference.

    – Jörg W Mittag
    Nov 24 '18 at 19:16


















1














I guess, meanwhile I've figured out why classes can't be used as modules in Ruby.

Or to be more specific: why classes can't be included/prepended.



It's the same reason as why Ruby does not support multiple inheritance:
to avoid ambiguity/complexity in the ancestors hierarchy.



After a short explanation of how multiple inheritance affects the ancestors hierarchy, I will explain why including/prepending classes would introduce multiple inheritance or/and similar complexities through the back door.





With single inheritance, the ancestors hierarchy of a given class is simply a chain.

The chain may be long or short, but it's always just a linear chain of ancestor classes:



File.ancestors
=> [File, IO, File::Constants, Enumerable, Object, Kernel, BasicObject]
Object.ancestors
=> [Object, Kernel, BasicObject]
BasicObject.ancestors
=> [BasicObject]


So looking up an instance variable or method is simple as that: look in the current class; if not found, go to the next ancestor and look there; if not found, go to the next ancestor and look there...





With multiple inheritance, the ancestors hierarchy can branch.

Hypothetically given



class A end
class B < A; end
class C < A; end

class D < B, C
end


produces the following ancestors graph of class D:



enter image description here



This increases complexity and ambiguity, it induces "the diamond problem":




  • Do instances of D share instance variables in class A inherited via B and inherited via C?

  • If they do not share them,

    we need an extended syntax to designate

    whether we want to access an instance variable in A via B or via C.


Ruby is designed to avoid this complexity. Hence, no multiple inheritance by design.





Including/prepending modules

is another mean of constructing/manipulating the ancestors hierarchy:



class MyBase < BasicObject; end
class C < MyBase; end
C.ancestors
=> [C, MyBase, BasicObject]

module IncludeMe end
C.include IncludeMe
C.ancestors
=> [C, IncludeMe, MyBase, BasicObject]

module PrependMe end
C.prepend PrependMe
C.ancestors
=> [PrependMe, C, IncludeMe, MyBase, BasicObject]

module Intermediate end
MyBase.include Intermediate
C.ancestors
=> [PrependMe, C, IncludeMe, MyBase, Intermediate, BasicObject]


Including/prepending modules only keeps the ancestors chain a simple chain.

No bad things happen.





Including/prepending classes



Now imagine IncludeMe, PrependMe, and Intermediate were not modules but classes.



To keep it simple, I will stick to one class only:



class PrependMe
def to_s
"Hello from prepended #{super}!"
end
end


Keep in mind that PrependMe inherits from Object by default:



PrependMe.ancestors
# => [PrependMe, Object, Kernel, BasicObject]


Also, pay attention to that fact that in Ruby it's not possible to create baseless classes (BasicObject is the only baseless class):



class BaselessClass < nil # Try it!
end # You'll fail.
# => TypeError: superclass must be a Class (NilClass given)


So, each class X (except BasicObject) has an ancestors chain with at least two parts,
always ending with BasicObject:



X.ancestors
# => [X, ..., BasicObject]
# the shortest possible chain is [X, BasicObject]
# the usual chain is [X, ..., Object, Kernel, BasicObject]


So, what should the ancestors hierarchy of class C look like after C.prepend PrependMe?



C.ancestors
=> [PrependMe, Object, Kernel, C, MyBase, BasicObject] ?
=> [PrependMe, C, Object, Kernel, MyBase, BasicObject] ?
=> [PrependMe, C, MyBase, Object, Kernel, BasicObject] ?
=> [PrependMe, C, MyBase, BasicObject] ? # Object and Kernel omitted on purpose


Or should the ancestors hierarchy even branch at PrependMe into an own branch for Object? With all the effects of the diamond problem.



One could reasonably argue for each of these options.

The answer depends on what you want to see as the result of (c = C.new).to_s.



Obviously, including/prepending classes would introduce ambiguity and complexity similar to or even worse than these of multiple inheritance. And as in the case of multiple inheritance, it's a deliberate decision of Ruby to avoid this.



Apparently, this is the reason why including/appending classes is forbidden in Ruby.






share|improve this answer


























  • You are assuming that including classes would work differently than including modules. If including classes worked the same way as including modules, the same linearization property would apply as for modules, and there would be no diamonds. Therefore, it would be perfectly possible to include classes the same way as modules. The problem with both this approach and yours, is that then the same language construct (classes) could be used for two different kinds of inheritance (classical class-based inheritance and linearized mixin inheritance) that work differently.

    – Jörg W Mittag
    Nov 24 '18 at 18:31











  • I guess (but I do not know) that this might be the reason to separate the two; to alleviate this confusion. (Although note that modules also serve a secondary purpose as namespaces, which interestingly is a trait that classes actually inherit from modules.) So, by my argument, there could even be three language constructs: namespaces, mixins, and classes.

    – Jörg W Mittag
    Nov 24 '18 at 18:33











  • Mixin inheritance is not multiple inheritance, it is, in fact, the exact dual: in multiple inheritance, a class appears once in the inheritance graph, and may have multiple superclasses, whereas in mixin inheritance, a class appears multiple times in the inheritance graph and each time has only one superclass.

    – Jörg W Mittag
    Nov 24 '18 at 18:35











  • The way mixins work in Ruby is as follows: Step 1: a new class M' is created whose method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer point to the same place in memory as the method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer of the module M. Step 2: the current superclass of C is made the superclass of M'. Step 3: M' is made the superclass of C. You could perform exactly those same steps regardless of whether M is a module or class, with exactly the …

    – Jörg W Mittag
    Nov 24 '18 at 19:15











  • … same results. Since M is copied to a class anyway in Step 1, it really doesn't matter at all for the algorithm whether M is a class or module, it will work exactly the same. The only thing classes have that modules don't, is a superclass pointer, but that is not used for M and overwritten for M', so it makes no difference.

    – Jörg W Mittag
    Nov 24 '18 at 19:16
















1












1








1







I guess, meanwhile I've figured out why classes can't be used as modules in Ruby.

Or to be more specific: why classes can't be included/prepended.



It's the same reason as why Ruby does not support multiple inheritance:
to avoid ambiguity/complexity in the ancestors hierarchy.



After a short explanation of how multiple inheritance affects the ancestors hierarchy, I will explain why including/prepending classes would introduce multiple inheritance or/and similar complexities through the back door.





With single inheritance, the ancestors hierarchy of a given class is simply a chain.

The chain may be long or short, but it's always just a linear chain of ancestor classes:



File.ancestors
=> [File, IO, File::Constants, Enumerable, Object, Kernel, BasicObject]
Object.ancestors
=> [Object, Kernel, BasicObject]
BasicObject.ancestors
=> [BasicObject]


So looking up an instance variable or method is simple as that: look in the current class; if not found, go to the next ancestor and look there; if not found, go to the next ancestor and look there...





With multiple inheritance, the ancestors hierarchy can branch.

Hypothetically given



class A end
class B < A; end
class C < A; end

class D < B, C
end


produces the following ancestors graph of class D:



enter image description here



This increases complexity and ambiguity, it induces "the diamond problem":




  • Do instances of D share instance variables in class A inherited via B and inherited via C?

  • If they do not share them,

    we need an extended syntax to designate

    whether we want to access an instance variable in A via B or via C.


Ruby is designed to avoid this complexity. Hence, no multiple inheritance by design.





Including/prepending modules

is another mean of constructing/manipulating the ancestors hierarchy:



class MyBase < BasicObject; end
class C < MyBase; end
C.ancestors
=> [C, MyBase, BasicObject]

module IncludeMe end
C.include IncludeMe
C.ancestors
=> [C, IncludeMe, MyBase, BasicObject]

module PrependMe end
C.prepend PrependMe
C.ancestors
=> [PrependMe, C, IncludeMe, MyBase, BasicObject]

module Intermediate end
MyBase.include Intermediate
C.ancestors
=> [PrependMe, C, IncludeMe, MyBase, Intermediate, BasicObject]


Including/prepending modules only keeps the ancestors chain a simple chain.

No bad things happen.





Including/prepending classes



Now imagine IncludeMe, PrependMe, and Intermediate were not modules but classes.



To keep it simple, I will stick to one class only:



class PrependMe
def to_s
"Hello from prepended #{super}!"
end
end


Keep in mind that PrependMe inherits from Object by default:



PrependMe.ancestors
# => [PrependMe, Object, Kernel, BasicObject]


Also, pay attention to that fact that in Ruby it's not possible to create baseless classes (BasicObject is the only baseless class):



class BaselessClass < nil # Try it!
end # You'll fail.
# => TypeError: superclass must be a Class (NilClass given)


So, each class X (except BasicObject) has an ancestors chain with at least two parts,
always ending with BasicObject:



X.ancestors
# => [X, ..., BasicObject]
# the shortest possible chain is [X, BasicObject]
# the usual chain is [X, ..., Object, Kernel, BasicObject]


So, what should the ancestors hierarchy of class C look like after C.prepend PrependMe?



C.ancestors
=> [PrependMe, Object, Kernel, C, MyBase, BasicObject] ?
=> [PrependMe, C, Object, Kernel, MyBase, BasicObject] ?
=> [PrependMe, C, MyBase, Object, Kernel, BasicObject] ?
=> [PrependMe, C, MyBase, BasicObject] ? # Object and Kernel omitted on purpose


Or should the ancestors hierarchy even branch at PrependMe into an own branch for Object? With all the effects of the diamond problem.



One could reasonably argue for each of these options.

The answer depends on what you want to see as the result of (c = C.new).to_s.



Obviously, including/prepending classes would introduce ambiguity and complexity similar to or even worse than these of multiple inheritance. And as in the case of multiple inheritance, it's a deliberate decision of Ruby to avoid this.



Apparently, this is the reason why including/appending classes is forbidden in Ruby.






share|improve this answer















I guess, meanwhile I've figured out why classes can't be used as modules in Ruby.

Or to be more specific: why classes can't be included/prepended.



It's the same reason as why Ruby does not support multiple inheritance:
to avoid ambiguity/complexity in the ancestors hierarchy.



After a short explanation of how multiple inheritance affects the ancestors hierarchy, I will explain why including/prepending classes would introduce multiple inheritance or/and similar complexities through the back door.





With single inheritance, the ancestors hierarchy of a given class is simply a chain.

The chain may be long or short, but it's always just a linear chain of ancestor classes:



File.ancestors
=> [File, IO, File::Constants, Enumerable, Object, Kernel, BasicObject]
Object.ancestors
=> [Object, Kernel, BasicObject]
BasicObject.ancestors
=> [BasicObject]


So looking up an instance variable or method is simple as that: look in the current class; if not found, go to the next ancestor and look there; if not found, go to the next ancestor and look there...





With multiple inheritance, the ancestors hierarchy can branch.

Hypothetically given



class A end
class B < A; end
class C < A; end

class D < B, C
end


produces the following ancestors graph of class D:



enter image description here



This increases complexity and ambiguity, it induces "the diamond problem":




  • Do instances of D share instance variables in class A inherited via B and inherited via C?

  • If they do not share them,

    we need an extended syntax to designate

    whether we want to access an instance variable in A via B or via C.


Ruby is designed to avoid this complexity. Hence, no multiple inheritance by design.





Including/prepending modules

is another mean of constructing/manipulating the ancestors hierarchy:



class MyBase < BasicObject; end
class C < MyBase; end
C.ancestors
=> [C, MyBase, BasicObject]

module IncludeMe end
C.include IncludeMe
C.ancestors
=> [C, IncludeMe, MyBase, BasicObject]

module PrependMe end
C.prepend PrependMe
C.ancestors
=> [PrependMe, C, IncludeMe, MyBase, BasicObject]

module Intermediate end
MyBase.include Intermediate
C.ancestors
=> [PrependMe, C, IncludeMe, MyBase, Intermediate, BasicObject]


Including/prepending modules only keeps the ancestors chain a simple chain.

No bad things happen.





Including/prepending classes



Now imagine IncludeMe, PrependMe, and Intermediate were not modules but classes.



To keep it simple, I will stick to one class only:



class PrependMe
def to_s
"Hello from prepended #{super}!"
end
end


Keep in mind that PrependMe inherits from Object by default:



PrependMe.ancestors
# => [PrependMe, Object, Kernel, BasicObject]


Also, pay attention to that fact that in Ruby it's not possible to create baseless classes (BasicObject is the only baseless class):



class BaselessClass < nil # Try it!
end # You'll fail.
# => TypeError: superclass must be a Class (NilClass given)


So, each class X (except BasicObject) has an ancestors chain with at least two parts,
always ending with BasicObject:



X.ancestors
# => [X, ..., BasicObject]
# the shortest possible chain is [X, BasicObject]
# the usual chain is [X, ..., Object, Kernel, BasicObject]


So, what should the ancestors hierarchy of class C look like after C.prepend PrependMe?



C.ancestors
=> [PrependMe, Object, Kernel, C, MyBase, BasicObject] ?
=> [PrependMe, C, Object, Kernel, MyBase, BasicObject] ?
=> [PrependMe, C, MyBase, Object, Kernel, BasicObject] ?
=> [PrependMe, C, MyBase, BasicObject] ? # Object and Kernel omitted on purpose


Or should the ancestors hierarchy even branch at PrependMe into an own branch for Object? With all the effects of the diamond problem.



One could reasonably argue for each of these options.

The answer depends on what you want to see as the result of (c = C.new).to_s.



Obviously, including/prepending classes would introduce ambiguity and complexity similar to or even worse than these of multiple inheritance. And as in the case of multiple inheritance, it's a deliberate decision of Ruby to avoid this.



Apparently, this is the reason why including/appending classes is forbidden in Ruby.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 25 '18 at 4:55

























answered Nov 24 '18 at 11:21









Min-Soo PipefeetMin-Soo Pipefeet

19510




19510













  • You are assuming that including classes would work differently than including modules. If including classes worked the same way as including modules, the same linearization property would apply as for modules, and there would be no diamonds. Therefore, it would be perfectly possible to include classes the same way as modules. The problem with both this approach and yours, is that then the same language construct (classes) could be used for two different kinds of inheritance (classical class-based inheritance and linearized mixin inheritance) that work differently.

    – Jörg W Mittag
    Nov 24 '18 at 18:31











  • I guess (but I do not know) that this might be the reason to separate the two; to alleviate this confusion. (Although note that modules also serve a secondary purpose as namespaces, which interestingly is a trait that classes actually inherit from modules.) So, by my argument, there could even be three language constructs: namespaces, mixins, and classes.

    – Jörg W Mittag
    Nov 24 '18 at 18:33











  • Mixin inheritance is not multiple inheritance, it is, in fact, the exact dual: in multiple inheritance, a class appears once in the inheritance graph, and may have multiple superclasses, whereas in mixin inheritance, a class appears multiple times in the inheritance graph and each time has only one superclass.

    – Jörg W Mittag
    Nov 24 '18 at 18:35











  • The way mixins work in Ruby is as follows: Step 1: a new class M' is created whose method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer point to the same place in memory as the method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer of the module M. Step 2: the current superclass of C is made the superclass of M'. Step 3: M' is made the superclass of C. You could perform exactly those same steps regardless of whether M is a module or class, with exactly the …

    – Jörg W Mittag
    Nov 24 '18 at 19:15











  • … same results. Since M is copied to a class anyway in Step 1, it really doesn't matter at all for the algorithm whether M is a class or module, it will work exactly the same. The only thing classes have that modules don't, is a superclass pointer, but that is not used for M and overwritten for M', so it makes no difference.

    – Jörg W Mittag
    Nov 24 '18 at 19:16





















  • You are assuming that including classes would work differently than including modules. If including classes worked the same way as including modules, the same linearization property would apply as for modules, and there would be no diamonds. Therefore, it would be perfectly possible to include classes the same way as modules. The problem with both this approach and yours, is that then the same language construct (classes) could be used for two different kinds of inheritance (classical class-based inheritance and linearized mixin inheritance) that work differently.

    – Jörg W Mittag
    Nov 24 '18 at 18:31











  • I guess (but I do not know) that this might be the reason to separate the two; to alleviate this confusion. (Although note that modules also serve a secondary purpose as namespaces, which interestingly is a trait that classes actually inherit from modules.) So, by my argument, there could even be three language constructs: namespaces, mixins, and classes.

    – Jörg W Mittag
    Nov 24 '18 at 18:33











  • Mixin inheritance is not multiple inheritance, it is, in fact, the exact dual: in multiple inheritance, a class appears once in the inheritance graph, and may have multiple superclasses, whereas in mixin inheritance, a class appears multiple times in the inheritance graph and each time has only one superclass.

    – Jörg W Mittag
    Nov 24 '18 at 18:35











  • The way mixins work in Ruby is as follows: Step 1: a new class M' is created whose method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer point to the same place in memory as the method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer of the module M. Step 2: the current superclass of C is made the superclass of M'. Step 3: M' is made the superclass of C. You could perform exactly those same steps regardless of whether M is a module or class, with exactly the …

    – Jörg W Mittag
    Nov 24 '18 at 19:15











  • … same results. Since M is copied to a class anyway in Step 1, it really doesn't matter at all for the algorithm whether M is a class or module, it will work exactly the same. The only thing classes have that modules don't, is a superclass pointer, but that is not used for M and overwritten for M', so it makes no difference.

    – Jörg W Mittag
    Nov 24 '18 at 19:16



















You are assuming that including classes would work differently than including modules. If including classes worked the same way as including modules, the same linearization property would apply as for modules, and there would be no diamonds. Therefore, it would be perfectly possible to include classes the same way as modules. The problem with both this approach and yours, is that then the same language construct (classes) could be used for two different kinds of inheritance (classical class-based inheritance and linearized mixin inheritance) that work differently.

– Jörg W Mittag
Nov 24 '18 at 18:31





You are assuming that including classes would work differently than including modules. If including classes worked the same way as including modules, the same linearization property would apply as for modules, and there would be no diamonds. Therefore, it would be perfectly possible to include classes the same way as modules. The problem with both this approach and yours, is that then the same language construct (classes) could be used for two different kinds of inheritance (classical class-based inheritance and linearized mixin inheritance) that work differently.

– Jörg W Mittag
Nov 24 '18 at 18:31













I guess (but I do not know) that this might be the reason to separate the two; to alleviate this confusion. (Although note that modules also serve a secondary purpose as namespaces, which interestingly is a trait that classes actually inherit from modules.) So, by my argument, there could even be three language constructs: namespaces, mixins, and classes.

– Jörg W Mittag
Nov 24 '18 at 18:33





I guess (but I do not know) that this might be the reason to separate the two; to alleviate this confusion. (Although note that modules also serve a secondary purpose as namespaces, which interestingly is a trait that classes actually inherit from modules.) So, by my argument, there could even be three language constructs: namespaces, mixins, and classes.

– Jörg W Mittag
Nov 24 '18 at 18:33













Mixin inheritance is not multiple inheritance, it is, in fact, the exact dual: in multiple inheritance, a class appears once in the inheritance graph, and may have multiple superclasses, whereas in mixin inheritance, a class appears multiple times in the inheritance graph and each time has only one superclass.

– Jörg W Mittag
Nov 24 '18 at 18:35





Mixin inheritance is not multiple inheritance, it is, in fact, the exact dual: in multiple inheritance, a class appears once in the inheritance graph, and may have multiple superclasses, whereas in mixin inheritance, a class appears multiple times in the inheritance graph and each time has only one superclass.

– Jörg W Mittag
Nov 24 '18 at 18:35













The way mixins work in Ruby is as follows: Step 1: a new class M' is created whose method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer point to the same place in memory as the method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer of the module M. Step 2: the current superclass of C is made the superclass of M'. Step 3: M' is made the superclass of C. You could perform exactly those same steps regardless of whether M is a module or class, with exactly the …

– Jörg W Mittag
Nov 24 '18 at 19:15





The way mixins work in Ruby is as follows: Step 1: a new class M' is created whose method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer point to the same place in memory as the method table pointer, class variable table pointer, constant table pointer, and instance variable table pointer of the module M. Step 2: the current superclass of C is made the superclass of M'. Step 3: M' is made the superclass of C. You could perform exactly those same steps regardless of whether M is a module or class, with exactly the …

– Jörg W Mittag
Nov 24 '18 at 19:15













… same results. Since M is copied to a class anyway in Step 1, it really doesn't matter at all for the algorithm whether M is a class or module, it will work exactly the same. The only thing classes have that modules don't, is a superclass pointer, but that is not used for M and overwritten for M', so it makes no difference.

– Jörg W Mittag
Nov 24 '18 at 19:16







… same results. Since M is copied to a class anyway in Step 1, it really doesn't matter at all for the algorithm whether M is a class or module, it will work exactly the same. The only thing classes have that modules don't, is a superclass pointer, but that is not used for M and overwritten for M', so it makes no difference.

– Jörg W Mittag
Nov 24 '18 at 19:16




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53449705%2fwhy-cant-classes-be-used-as-modules%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Costa Masnaga

Fotorealismo

Sidney Franklin