On Wed, Mar 29, 2023, at 2:25 PM, Rokas Šleinius wrote: > First, I'm pretty sure I agree now that enums should *not* be > `extend`-ed as regular classes, there's a fundamental difference as > you state - the extended enum is a fundamentally different THING as it > can hold different values - when an enum's sole purpose is to hold one > of the set of values. > > That is to say - for most implementations that's the desired case. > > I'll refer to the solution to the OP problem as ECS, Error Code System. > > For ECS, the most elegant, but also the only, solution I can come up > with - is the kind of enum that you also suggest, a `base-enum`. > `abstract enum`? > > Let me try again to describe the problem that I came here with that is > unsolvable by union types: > > 1. ECS is a *generic* component. I want to plug it into any working > system (like an existing cms). > 2. Invoking it must provide a unique ErrorCode. The existing cms has > to create an enum that will be accepted as `type: ErrorCode`. It > cannot edit the existing ErrorCode as it is part of ECS and not the > cms. > 3. There is NO special handling for the custom ErrorCode's that the > users of ECS create. We are just using the enums inferred value. > > I will keep processing this situation further, but for now it seems to me like > > 1. Extending enums is fundamentally flawed. > 2. Basing enums off of other enums has valid usage scenarios. > > In that case, and, again, this needs way more thought to it, it's not > such a "generic way forward" that seemed to me at first and might only > provide marginal value at the cost of type complexity and that is most > probably, unfortunately, not worth it... > > (unless `abstract enum` might make sense, but my brain needs some time > off of this problem for now) > > Thank you for such a thought out discussion, everyone!
1) Please don't top-post. 2) Some good reading material for this topic, both on enums and on error handling: https://peakd.com/hive-168588/@crell/on-the-use-of-enums https://peakd.com/hive-168588/@crell/much-ado-about-null They're not short, but that's because they are complete. :-) 3) In the error code case, the answer is to leverage the fact that enums *do* support interfaces. interface ErrorCode { public function message(): string; } enum CommonErrors: string implements ErrorCode { case YourFault = 'You screwed up'; case OurFault = 'We screwed up'; public function message(): string { return $this->value; } } readonly class SomeoneElseError implements ErrorCode { public function __construct(privateUser $atFault) {} public function message(): string { return sprintf('%s screwed up', $this->atFault->name()); } } Now an error handling system can type against the ErrorCode interface, common errors have trivially simple enum values you can use, but you can also make your own. In this case, you're *not* using an enum as a limited-set; you're taking advantage of it being a way to predefine singleton objects. I would consider this a completely valid use of enums, and actually do something very similar in my serialization library: https://github.com/Crell/Serde/blob/master/src/Renaming/RenamingStrategy.php https://github.com/Crell/Serde/blob/master/src/Renaming/Cases.php https://github.com/Crell/Serde/blob/master/src/Renaming/Prefix.php 4) As others have said, extending enums is a bad idea with lots of reasons it's a bad idea, both conceptual and pragmatic. However, I would be open to discussing a `use` for enums to import cases from one enum into another. There's two concerns with that to consider: A) What happens to methods on the imported enum? Are they pulled in as well? Do interface definitions carry over? I don't know. B) The long-term goal is to expand Enums to include associated values (https://wiki.php.net/rfc/tagged_unions, although that's a bit out of date now so don't take it as a roadmap). How would `use`-ing an enum within another enum affect that? I have no idea, off hand. --Larry Garfield -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php