-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convert single class inheritance to composition #33
Comments
Summary:
Requested Outcome: |
Private members are not accessible by subclasses in Neat. (I don't think?) I haven't added Generally speaking, Neat deemphasizes inheritance to begin with. I think the dangers of inheritance are overstated because people use it for things where it doesn't belong. As a result, I haven't run into any of the issues you speak of, but they seem to me like issues of communication more so than language design. Also, sumtypes let you upgrade a class by just writing a new BaseclassV2 and putting it in a sumtype with Baseclass in your library. This should significantly reduce the overhead. I don't follow your point about dependency injection. You should be defining an interface, that both your implementation and the test implementation inherit from. I'm not planning to make any of those changes, as my experience with OOP doesn't bear them out. If you want to add composition-based features to the language, be my guest, macros are right there - if there's some missing macro feature that prevents parity between macro OOP and builtin OOP please let me know and I'll add it. |
Thanks for the response!
Sounds good - maybe consider not adding them at all?
Thought experiment: What's a bright line test where inheritance does / does not belong? It's none of my business, though having no inheritance will probably simplify the variable scope resolution, type checking (the type is the type, it's unique and it's not related to any other type other than through interfaces), type casting (no dynamic_cast issues without vtables/RTTI), any reflection based code (no need for special "getSuperClass()" type methods), the compiler's function binding code (static dispatch only) and you'll guarantee a language without hidden vtable function pointers. Bonus: macro type system will be clean.
Consider what would happen to generics, reflection and macros if this is the path being chosen.
Yes - we both agree. However, if Cat inherits from Animal, there's no way to stub out the Animal's code in the test code's construction of Cat - the Cat constructor will hard code the Animal constructor. If the test wants to stub out the Animal implementation, it is disallowed by the language. This isn't a language feature, it's a bug. (Minor point: Cat inherits from Animal is the same as Cat(Animal a), the 2nd one is dependency injection compatible, the first one has no way to inject an Animal type in the constructor - the closest we can get are:
Fair enough. Here, I have the opportunity to influence your thinking at the early stages of a promising project - a stage where I believe some of my effort will yield disproportionate long term results. My goal here is to not be a crank. If I'm anywhere close to doling out gray bearded advice in this area, I will have been successful. In summary, my last pieces of advice are:
I wish you the best! Neat is promising. Please feel free to close out this ticket. Cheers! [1] https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Interfaces |
Single class inheritance is functionally identical to a delegation pattern (the hidden *this is made explicit). The problem with inheritance is that it couples state and implementation by breaking encapsulation.
The alternative is to be inheritance free and when inheritance has to be simulated, use a delegation pattern.
Eg. (in pseudocode)
it’s very important to not allow private member leakage as refactors of a widely used base class with non trivial internal state become impossible because of long distance stateful couplings of internal state. I’ve seen this several times in widely used codebases at Google while working on Search and all such refactors of class hierarchies were close to no-go as they were 3 levels of stateful class couplings and no one could predict if a local change in the internals of the base class was safe (and thread safe) because of the internal accesses done by “derived!!! classes”.
Even if the code was completely audited for safety, there could be a PR in flight that relied on the old thread safety assumptions inside the internal code and a silent corruption due to that would be pretty bad.
To make this a bit more concrete - I’m sure you can understand that if a stateful class was implementing a docId counter, such a piece of code would be widely reused with small variations and changing the base class would be an extremely delicate operation.
For reasons like this and others, the modern trend is towards trait delegation rather than traditional inheritance.
Here are 2 blog posts that I think you might find helpful in the context of this issue:
https://www.divye.in/2019/06/modern-programming-never-use.html - OOP inheritance = conflation of type composition and implementation composition in the implementation space (bad! breaks dependency injection).
https://www.divye.in/2022/02/public-static-is-harmful-it-has-no-home.html (slightly off topic, the main link being that this is an example where type-composition and code composition were conflated in the space of types - it's the inverse problem of inheritance).
The only way to avoid both of these traps is to ensure that Types and Implementations can't be coupled. The correct relationship between them is through the delegation pattern as above.
Thanks for taking the time to read this. I realize this is a major change that’s being proposed. I hope you’ll consider it with due care. The best and only time to resolve something like this is early in the evolution of the language. I really liked how Neat is structured. Like you, I seek an alternative to the C++ mess and Rust doesn’t cut it for me.
Thanks and best wishes!
Divye
The text was updated successfully, but these errors were encountered: