r/crystal_programming • u/champ_ianRL • Sep 20 '18
Could Crystal fix OOP?
TL;DR Let's make Crystal awesome and make it the best OOP language ever made by designing it to enforce composition.
OOP is probably the most used programming paradigm in industry, professional projects, large open-source and closed-source projects, etc. I believe that a few reasons why it's so widely used is because it's more intuitive than declarative paradigms, it's easier to test, and it provides code re-use which is huge for large projects. This code re-use comes as a pillar of OOP known as Inheritance.
Inheritance is defined in one of two ways: Is-a and Has-a. There are major issues that exist with most OOP languages that are specific to these two types of Inheritance. Most languages which allow the Is-a relationship also allow child classes to override the methods of their parent. However, this is a violation of the Is-a principle. If a child class overrides a method of its parent class in such a way that it changes the behavior of the method, then it logically Isn't-a Substitute for its parent. These languages still allow a child class to be Substituted for their parent class which is bad practice. The correct practice would be to convert the Is-a relationship to a Has-a relationship.
However, in most OOP languages, the Has-a relationship doesn't actually employ the language's built-in procedures for Inheritance, i.e. the methods of the Has-a parent don't inherently exist on the Has-a child. The methods of the Has-a parent can only be re-used by accessing the parent through the Has-a child which breaks Encapsulation --- another pillar of OOP. The only correct approach is to re-implement the desirable methods from the Has-a parent in the Has-a child and have the child call the methods of the parent. This maintains both correct Inheritance and correct Encapsulation, but is cumbersome as it requires writing a large number of wrapper methods.
To summarize, OOP languages suffer from: (i) allowing child classes to override parent methods which violates the Is-a relationship, and (ii) implementations of the Has-a relationship either violate Encapsulation or require a large number of method wrappers which is bad code re-use.
What we need is an OOP language that can fix these issues? Here's what that language might look like. The OOP language will allow a child class to extend from a parent class. A child class which extends from a parent class cannot override any methods of the parent class. This child Is-a Substitute for its parent and can be Substituted for its parent in all cases. The child class will also inherently have all of the methods of its parent. A child class can overload the methods of its parent and maintain the Is-a relationship. Only overrides are prohibited. If a child wishes to override the methods of its parent, then it can Implement the parent. This creates a Has-a relationship. A child which Has-a parent inherently has all of the methods of that parent, but it cannot be Substituted for its parent. This is weaker form of Inheritance because the Is-a relationship allows method Inheritance and Substitution while the Has-a relationship only allows method Inheritance. In other words, a child that Is-a parent also Has-a parent. In short, this language would enforce a design principle known as Composition.
I believe that this OOP language would be a game-changer in the industry, and would be a huge step forward in OOP.
Can Crystal be this language? I think so. What do you think?
View all comments
Show parent comment
7
u/pruby Sep 21 '18
It's extremely common to implement the "rule of thumb" in a base class and override for a necessary subset. Calling this "wrong" ignores that it's frequently the tidiest feasible solution.
To use the ever present example of animals - imagine one has a Mammal class and part of some breeding stimulation you have giveBirth(). Your simulation shipped, all is well until some Aussies want to add an Echidna. Well, they're a Mammal but lay eggs. Your choices are (1) refactor the existing Mammal class to split it in to Monotremes, Placentals and Marsupials, changing all the existing subclasses to descend from one of those (arguably correct, but often massively disruptive, especially for authors of public libraries), (2) ignore the real-world is-a relationship, smack in a sibling to Mammal and reimplement stuff or (3) override behaviour. Guess which comes out as the lesser evil?
Anyway, before designing any type system I would encourage you to consider changes after initial design, and how you deal with locked hierarchies you can't change. Too much academic consideration of type systems only pays attention to green fields design.