Ok so I am trying to find the argumentation here: >This is by design. >The point of enums is to be limiting.
This is clearly an assumption. That statement is not in the docs or RFC, nor such an oversimplification makes logical sense to me, but maybe you have a source to back it up..? >Re: problem in the OP >You are approaching the design of this in a fundamentally wrong way. The described problem is dealing with error *codes* not *types*, which means that each error is assigned a new *code*. We are dealing with anticipated errors here, things like validation, data integrity, 3rd party API breakage etc. e.g.: user gets code 456 trying to ship out package, it says package not paid, user says he looked it up, its paid. The developer has a very concrete lead to the problem - lookup usages of `ErrorCodes::Code_456`. The set in question is *finite*, as you say of course, but much larger and different in principle than in the solution you are proposing. Problem in OP is looking for a way for end users to keep adding new domains to the error codes passed to the parent system to be handled. The actual, real requirements are: 1. Ease of management of codes: simplicity of adding *new* codes to the set, easy lookup, simplicity of making sure a code is not used elsewhere before using it. All perfectly fulfilled by enums. 2. Reusing the parent system for different modules, using the parent system in two different packages I want to opensource. Open Sourcing the parent system.. Rowan Tommins is right on the target however, that is amazing feedback for the desired functionality, excellent distillment of logic and all correct points! I will take some time to think about that. On Wed, 29 Mar 2023 at 15:30, G. P. B. <george.bany...@gmail.com> wrote: > > On Wed, 29 Mar 2023 at 09:31, Rokas Šleinius <rave...@gmail.com> wrote: >> >> Enums were a very useful addition to PHP, however one aspect of them is >> neither >> explicitly documented - or seemingly even talked about. >> >> Enums were implemented as final so they cannot be extended nor can extend >> anything else. > > > This is by design. > Enumerations are in type theory parlance sum types. > Objects in PHP, in general, are what are called product types. > >> >> From a user perspective it's surprising - and actually limiting. > > > The point of enums is to be limiting. > >> >> USAGE EXAMPLE: >> I am making an error management system: each error presented to the user >> must have a unique code visible. >> >> ```php >> class SystemError >> { >> public function __construct( >> private string $errorText, >> private ErrorCode $code >> ) { >> } >> >> public function __toString(): >> { >> return $this->errorText . ' ' . $this->code->toString(); >> } >> } >> // ... >> >> enum ErrorCode >> { >> case Code_1; >> case Code_2; >> >> public function toString(): string >> { >> return 'Error code:' . substr($this->name, strlen('Code_')); >> } >> } >> ``` >> >> Now I want to modify it to support different modules with different >> namespaces for >> errors, e.g. an ApiError, simple enough: >> >> ```php >> enum BaseErrorCode >> { >> // ... >> } >> >> enum ErrorCode extends BaseErrorCode >> { >> case Code_1; >> case Code_2; >> >> // ... >> } >> >> enum ApiErrorCode extends BaseErrorCode { >> // ... >> function toString(): string >> { >> return 'Error code:API-' . substr($this->name, strlen('Code_')); >> } >> } >> ``` >> >> This results in a syntax error. > > > You are approaching the design of this in a fundamentally wrong way. > Enums, as sum types, are meant to enclose a *finite* number of states. > If you want to expand those finite number of states, the solution is to use a > union type. > > Let's say you have those 3 enums: > > enum GenericErrors { > case ErrorType1; > case ErrorType2; > } > > enum FileErrors { > case FileErrorType1; > case FileErrorType2; > case FileErrorType3; > } > > enum NetworkErrors { > case NetworkErrorType1; > case NetworkErrorType2; > case NetworkErrorType3; > } > > And you have a function that can fail with either a generic error or a file > error then you can be explicit by having: > > function foo(): T|GenericErrors|FileErrors { /* Do something or Fail */ } > > This signature provides you with great type safety and can be checked via > static analysis that *all* error cases are handled. > Moreover, you know you don't need to handle NetworkErrors. > Extending an Enum *loses* you this type safety, because now ff you say > something returns a GenericErrors well who knows if you cover all cases > because someone might have ad-hock added a new one. > > PHP's type system is not perfect yet, but it has become powerful enough that > you can do things like what I just described. > Ideally we would have generics, and you could use a Result/Either Monad > (fancy word for "wrapper") to have a return type of Result<T, > GenericErrors|FileErrors> > > Use the type system properly instead of trying to shove everything into an > "Inheritance" view of things. > Union types are not "bad", they are another way of creating a sum type. > (You could, with a lot of effort, create "proper" enumerations since PHP 8.0 > by using union types and singleton classes.) > > >> PROPOSAL: >> >> Enums should be able to extend other enums. >> >> For a complete wishlist, add: >> * abstract enums; >> >> * enums allowed to implement interfaces; > > > Enums can already implement interfaces: https://3v4l.org/tKtQ0 > >> >> However since I have no experience in PHP source code, I can only >> provide the test suite for a possible PR this might have :( >> >> Do you think this is likely to get implemented? > > > I dear hope so not. > Breaking fundamental type theoretical assumptions is terrible. > > Best regards, > > George P. Banyard -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php