5 ms·
Rigid, "family tree"-style inheritance as in classical OOP is pretty much garbage. "A cow is a mammal is an animal" is largely useless for the day to day work w
by echelon 2y ago
Rigid, "family tree"-style inheritance as in classical OOP is pretty much garbage. "A cow is a mammal is an animal" is largely useless for the day to day work we do except in extremely well-planned, large and elaborate ontologies -- something you typically only see in highly structured software like windowing systems. It just isn't useful for the majority of our work.
"Trait/Typeclass"-style compositional inheritance as in Rust and Haskell is sublime. It's similar to Java interfaces in terms of flexibility, and it doesn't enforce hierarchical rules [1]. You can bolt behaviors and their types onto structures at will. This is how OO should be.
I put together a visual argument on another thread on HN a few weeks ago:
https://imgur.com/a/class-inheritance-vs-traits-oop-isnt-bad-inheritance-is-bad-bmdUV3H https://imgur.com/a/class-inheritance-vs-traits-oop-isnt-bad...
[1] Though if you want rules on bounds and associated types, you can have them.
- DaiPlusPlus 2y ago> "Trait/Typeclass"-style compositional inheritance as in Rust and Haskell is sublime. It's similar to Java interfaces in terms of flexibility, and it doesn't enforce hierarchical rules. Yes-and-no. Interfaces still participate in inheritance hierarchies (`interface Bar extends Foo`), and that's in a way that prohibits removing/subtracting type members (so interfaces are not in any way a substitute for mixins). Composition (of interfaces) can be used instead of `extends`, but then you lose guarantees of reference-identity - oh, and only reference-types can implement interfaces which makes interfaces impractical for scalars and unusable in a zero-heap-alloc program. Interface-types can only expose virtual members: no public fields - which seems silly to me because a vtable-like mechanism could be used to allow raw pointer access to fields via interfaces, but I digress: so many of these limitations (or unneeded functionality) are consequences of the JVM/CLR's design decisions which won't change in my lifetime. Rust-style traits are an overall improvement, yes - but (as far as my limited Rust experience tells me) there's no succinct way to tell the compiler to delegate the implementation of a trait to some composed type: I found myself needing to write an unexpectedly large amount of forwarding methods by hand (so I hope that Rust is better than this and that I was just doing Rust the-completely-wrong-way). Also, oblig: https://boxbase.org/entries/2020/aug/3/case-against-oop/ https://boxbase.org/entries/2020/aug/3/case-against-oop/
- int_19h 2y agoHow are interfaces with ability to provide default implementations for members (which both C# and Java allow today) not a substitute for mixins? "Only reference types can implement interfaces" is simply not true in C#. Not only can structs implement them, but they can also be used through the interface without boxing (via generics).
- AstralStorm 2y agoThey just cannot access any field in the class itself. Which means... the default implementations fail to actually implement anything but the simplest methods or you need to expose API-private things via getters violating open-closed principle. (If you merge multiple interfaces, the implementations of the methods have to match. You end up with even more special getters for each one sometimes.)
- int_19h 2y agoIn C# interface members can be implemented explicitly, which means that: 1) they are not visible on the object itself, only on interface-typed references to it, and 2) implementations of methods from multiple interfaces don't need to match since they live in separate namespaces. It's true that you can't access private members (not just fields) on `this` from the mixin interface. But explicit implementations of members mean that only someone explicitly downcasting the object will get access to those members, so accidental access is not an issue.
- DaiPlusPlus 2y ago> How are interfaces with ability to provide default implementations for members (which both C# and Java allow today) not a substitute for mixins? Those default-implementations are only accessible when the object is accessed via that interface; i.e. they aren't accessible as members on the object itself. Furthermore, interfaces (still) only declare (and optionally define) vtable members (i.e. only methods, properties, and events - which are all fundamentally just methods), not fields or any kind of non-static state, whereas IMO mixins should have no limitations and should behave the same as though you copied-and-pasted raw code.
- zozbot234 2y agoRust actually allows one to express "family tree" object inheritance quite cleanly via the generic typestate pattern. It isn't "garbage", it totally has its uses. It is however quite antithetical to modularity: the "inheritance hierarchy" can only really be understood as a unit, and "extensibility" for such a hierarchy is not really well defined. Hence why in practice it mostly gets used in cases where the improved static checking made possible by the "typestate" pattern can be helpful, which has remarkably little to do with "OOP" design as generally understood.