On Jul 11, 2023, at 13:42, Larry Garfield <la...@garfieldtech.com> wrote: > > On Tue, Jul 11, 2023, at 5:28 PM, Robert Landers wrote: >> >> IMHO, using a shared base class reflects the inheritance better >> because they are siblings (at least these appear to be logical >> siblings out of context, IMHO) and should look like siblings in a >> class diagram. Their current dis-jointed-ness strikes me as odd and >> the usage of traits in this way strikes me as an anti-pattern. But I >> came from a PHP world where traits were nearly forbidden or used very >> sparingly. > > Traits are a surgical tool with a narrow set of uses, and systems that > over-use them cause problems. (You know which framework you are...) But > this is one of their main valid uses. > > In context, the classes are not siblings, and it would be incorrect for them > to extent from a base class. They just both happen to implement the same > *interface*, but that the same as having the same "is a special case of" > relationship with a base class. > > Having a common base class instead would, as noted before, mean as soon as I > added a third "carries" option, I'd have to add four more base classes to > cover all combinations. It quickly gets absurd, and those base classes have > no valid type usage; they're purely there as an alternative to copy-paste. > > Using inheritance as an alternative to copy-paste is the wrong way(tm) to do > it. Both inheritance and copy-paste. Freshman CS classes still love to talk > about inheritance as a great thing for code reuse but... it's really not, and > many of the 21st century languages have been drifting away from that. > > Traits/default-method-interfaces are a better alternative that doesn't > conflate "copy-paste avoidance" with "is a special case of." Honestly, I > almost never use class inheritance in my greenfield code these days.
I'm stating my +1 for this feature (though I can't vote since I lack vote karma), and also want to point that this is a feature in Swift as well, and used extensively through its standard library. (The actual mechanics are somewhat different, and significantly more powerful, both for good and ill. I'm handwaving a bit to not get bogged down in details that don't/can't matter for PHP.) A basic example in Swift's standard library is the Equatable and Comparable interfaces. Both have partial implementations provided. Between the two, if you implement the == and < operations, the stdlib provides !=, <=, >, and >= automatically, but you can override them if desired by providing an explicit implementation. This is used pretty much everywhere; most of the builtin types (like Int, Float, String, and Array, which otherwise have no relation to each other and definitely don't inherit from some base type) implement Equatable and Comparable. Another example is the Sequence interface, which comes with a set of default implementations for standard sequence algorithms, such as map, first, min/max, contains, etc. This allows all types of sequences to have the functionality expected of a sequence without needing to redundantly implement basic operations (unless desired) or participate in inheritance. This clear distinction between what a type _is_ (its type and inheritance tree) and what a type _can do_ (the interfaces it implements) is really important when you start piling on the interfaces. Swift's standard library comes with a laundry list of interfaces that provide partial or complete default implementations, and it's entirely reasonable to combine these together. Consider, for example, a type that implemented each of Sequence, Collection, Encodable, Decodable, Equatable, and Hashable (such as Array or Dictionary). Creating a sane inheritance tree for that would be difficult. (And that's before considering that an array or dictionary should itself be Encodable/Decodable/Equatable/Hashable if its elements (and keys) have those properties. In Swift, the implementation for Array and Dictionary is largely the details of managing data storage. Useful algorithms get automatically inherited from default implementations from their many interfaces without needing a common parent class for both arrays and dictionaries, which are both structs which can't have inheritance anyway.) I'll also point out that (in both PHP and Swift) enums can't participate in inheritance, but can implement interfaces. If one wanted to provide a default implementation for an interface used an enum, an abstract class isn't an option at all. Anecdotally, outside of framework-mandated inheritance, the vast majority of my types in Swift are structs, enums, and classes with no inheritance, and many have interfaces that have some default implementation provided. Most of my PHP classes that have inheritance would be better served with interfaces + default implementations; they use inheritance only because it is currently the least bad way to make them work. Inheritance doesn't really capture the relationship between them (which is that there is none, save for their shared interface). -John -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php