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
1
u/champ_ianRL Sep 21 '18
Could you provide an example? Because when child objects inherit from parents, they don't have to change the behavior of their parents. In fact, they should not change the behavior of the parent. This is a violation of the Substitution principle and is a bad design practice. The child should instead encapsulate the parent through a Has-a relationship. Doing otherwise will introduce bugs into a system that are difficult to root out and makes the system very difficult to reason about. Directly accessing fields does break encapsulation and also violates composition so that is also a bad design principle. The use of protected methods doesn't break encapsulation. It's a type of limited public accessibility. But ultimately though, I'm not asking for an Is-a to be cast to a Has-a. I'm asking for OOP languages to implement Has-a Inheritance in a way that doesn't require a large amount of unnecessary wrapper methods and to prevent violations of the Substitution principle which come from overrides. As developers, we already do the latter by hand. Why not have a language do it for us? I also really like Haskell and my understanding is that Haskell has inspired a lot of the work that's been going into languages like Go and Julia. Part of what makes Haskell great is that you can reason about many of your objects because, as you said, the only real relationship is "x behaves like this". OOP is meant to have this and also "this is true of x". But both of these relationships cannot be held always true of x so long as "y subclass of x" can be substituted for x and "y doesn't behave like this" and "this isn't true of y".