Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to offer you round 2 on enumerations. This is in the spirit of the previous discussion, but based on that discussion a great deal has been reworked. The main change is that Enumeration Cases are now object instances of the Enumeration class rather than their own class. Most of the other changes are knock-on effects of that.
Of particular note:
- Cases may not have methods or constants on them. They're just dumb values.
- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given how much was updated.
https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in reflection, and some opcache bugs that Ilija is still stomping on.
There are a few outstanding questions listed that we would like feedback on. We're not entirely certain which direction to go with them, for reasons explained in the RFC. Input on those is especially welcome.
Happy New Year. May it be enumerable.
--
Larry Garfield
larry@garfieldtech.com
Hi Larry, thank you for the updated RFC!
I love it, and having played with the implementation, I can say I love it
so far as well.
I have one suggestion regarding reflection:
Shouldn't ReflectionCase expose an additional getInstance() method, that
would return the case instance, such as Suit::HEARTS?
Also, I noticed 2 typos in the code samples:
- in "Enumerated Methods", 1st code block: missing "implements Colorful"
after "enum Suit" - in "Enumerated Methods", 2nd code block: missing "public function
color()" after "private function __construct() {}"
Finally, I got a segmentation fault while trying to use what I think is an
unsupported syntax (removing the ":string" from a scalar enum), where is
the correct place to report this? The GitHub PR?
Good luck with the RFC!
- Benjamin
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to offer
you round 2 on enumerations. This is in the spirit of the previous
discussion, but based on that discussion a great deal has been reworked.
The main change is that Enumeration Cases are now object instances of the
Enumeration class rather than their own class. Most of the other changes
are knock-on effects of that.Of particular note:
- Cases may not have methods or constants on them. They're just dumb
values.- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given how
much was updated.https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in
reflection, and some opcache bugs that Ilija is still stomping on.There are a few outstanding questions listed that we would like feedback
on. We're not entirely certain which direction to go with them, for
reasons explained in the RFC. Input on those is especially welcome.Happy New Year. May it be enumerable.
--
Larry Garfield
larry@garfieldtech.com--
To unsubscribe, visit: https://www.php.net/unsub.php
Hi Larry, thank you for the updated RFC!
I love it, and having played with the implementation, I can say I love it
so far as well.I have one suggestion regarding reflection:
Shouldn't ReflectionCase expose an additional getInstance() method, that
would return the case instance, such as Suit::HEARTS?
I... thought we had that in there. Did I remove the wrong method? I know it's in there somewhere. Let me double check with Ilija on what it's supposed to be called. Will update when that's sorted out.
Also, I noticed 2 typos in the code samples:
- in "Enumerated Methods", 1st code block: missing "implements Colorful"
after "enum Suit"- in "Enumerated Methods", 2nd code block: missing "public function
color()" after "private function __construct() {}"
Both fixed, thanks.
Finally, I got a segmentation fault while trying to use what I think is an
unsupported syntax (removing the ":string" from a scalar enum), where is
the correct place to report this? The GitHub PR?
On the PR, please. Sounds like we need a few more negative tests to make sure it fails gracefully.
--Larry Garfield
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to offer you round 2 on enumerations. This is in the spirit of the previous discussion, but based on that discussion a great deal has been reworked. The main change is that Enumeration Cases are now object instances of the Enumeration class rather than their own class. Most of the other changes are knock-on effects of that.
Of particular note:
- Cases may not have methods or constants on them. They're just dumb values.
- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given how much was updated.
I did and the RFC looks really awesome :+1:
I don't have time to test the implementation but I noticed one thing:
If the enumeration is not a Scalar Enum, the array will be packed
(indexed sequentially starting from 0). If the enumeration is a Scalar
Enum, the keys will be the corresponding scalar for each enumeration.
I don't think using the scalar values as keys is a good idea. What
happens if we want to support scalar float values? (Why are they
actually not supported in the first place?)
Also I think it's more natural if both enum types return a
zero-indexed-array of cases.
https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in reflection, and some opcache bugs that Ilija is still stomping on.
There are a few outstanding questions listed that we would like feedback on. We're not entirely certain which direction to go with them, for reasons explained in the RFC. Input on those is especially welcome.
Happy New Year. May it be enumerable.
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to offer you round 2 on enumerations. This is in the spirit of the previous discussion, but based on that discussion a great deal has been reworked. The main change is that Enumeration Cases are now object instances of the Enumeration class rather than their own class. Most of the other changes are knock-on effects of that.
Of particular note:
- Cases may not have methods or constants on them. They're just dumb values.
- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given how much was updated.
I did and the RFC looks really awesome :+1:
I don't have time to test the implementation but I noticed one thing:
If the enumeration is not a Scalar Enum, the array will be packed
(indexed sequentially starting from 0). If the enumeration is a Scalar
Enum, the keys will be the corresponding scalar for each enumeration.I don't think using the scalar values as keys is a good idea. What
happens if we want to support scalar float values? (Why are they
actually not supported in the first place?)
That's why floats are not supported, in fact, because what happens to them when they are made into an array key is non-obvious. (PHP would say to convert to a string, but that's always fussy with possible data loss in some cases, etc.) We decided to just avoid that problem until/unless someone found a good use case for float enums. No all languages support them as is, so there is precedent.
Also I think it's more natural if both enum types return a
zero-indexed-array of cases.
The goal is to make it easy to work with them, and having a clean lookup map readily available is very convenient. If you don't care about the scalar equivalent then you can safely ignore them. If you do want them, then you have a lookup table ready-made for you. That's the logic we were working from.
--Larry Garfield
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to offer you round 2 on enumerations. This is in the spirit of the previous discussion, but based on that discussion a great deal has been reworked. The main change is that Enumeration Cases are now object instances of the Enumeration class rather than their own class. Most of the other changes are knock-on effects of that.
Of particular note:
- Cases may not have methods or constants on them. They're just dumb values.
- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given how much was updated.
I did and the RFC looks really awesome :+1:I don't have time to test the implementation but I noticed one thing:
If the enumeration is not a Scalar Enum, the array will be packed
(indexed sequentially starting from 0). If the enumeration is a Scalar
Enum, the keys will be the corresponding scalar for each enumeration.I don't think using the scalar values as keys is a good idea. What
happens if we want to support scalar float values? (Why are they
actually not supported in the first place?)
That's why floats are not supported, in fact, because what happens to them when they are made into an array key is non-obvious. (PHP would say to convert to a string, but that's always fussy with possible data loss in some cases, etc.) We decided to just avoid that problem until/unless someone found a good use case for float enums. No all languages support them as is, so there is precedent.Also I think it's more natural if both enum types return a
zero-indexed-array of cases.
The goal is to make it easy to work with them, and having a clean lookup map readily available is very convenient. If you don't care about the scalar equivalent then you can safely ignore them. If you do want them, then you have a lookup table ready-made for you. That's the logic we were working from.
I don't have a really good use-case for float values. It just seems
weird to me that a ScalarEnum doesn't support all scalars.
Using the enum value as array key for cases()
works with your current
proposal but if we later want to allow floats, bool whatever then we got
a food gun.
You already provide a lookup mechanism with MyEnum::from()
- I don't
see a real use-case for proving a pre build map. The main use case I see
is to list all possible enum values but this doesn't require a map and a
zero-indexed-array would also be more performant with packed arrays
(correct me if I'm wrong).
--Larry Garfield
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to offer you round 2 on enumerations. This is in the spirit of the previous discussion, but based on that discussion a great deal has been reworked. The main change is that Enumeration Cases are now object instances of the Enumeration class rather than their own class. Most of the other changes are knock-on effects of that.
Of particular note:
- Cases may not have methods or constants on them. They're just dumb values.
- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given how much was updated.
I did and the RFC looks really awesome :+1:I don't have time to test the implementation but I noticed one thing:
If the enumeration is not a Scalar Enum, the array will be packed
(indexed sequentially starting from 0). If the enumeration is a Scalar
Enum, the keys will be the corresponding scalar for each enumeration.I don't think using the scalar values as keys is a good idea. What
happens if we want to support scalar float values? (Why are they
actually not supported in the first place?)
That's why floats are not supported, in fact, because what happens to them when they are made into an array key is non-obvious. (PHP would say to convert to a string, but that's always fussy with possible data loss in some cases, etc.) We decided to just avoid that problem until/unless someone found a good use case for float enums. No all languages support them as is, so there is precedent.Also I think it's more natural if both enum types return a
zero-indexed-array of cases.
The goal is to make it easy to work with them, and having a clean lookup map readily available is very convenient. If you don't care about the scalar equivalent then you can safely ignore them. If you do want them, then you have a lookup table ready-made for you. That's the logic we were working from.
I don't have a really good use-case for float values. It just seems
weird to me that a ScalarEnum doesn't support all scalars.Using the enum value as array key for
cases()
works with your current
proposal but if we later want to allow floats, bool whatever then we got
a food gun.
Forgot to mention on (virtiually) adding generics to the game the method
cases(): array;
http://www.php.net/arraywould be described as
cases(): array<int, static>;
on UnitEnum but cases(): array<string|int, static>;
on ScalarEnum which is not compatible for
reasons and I think (even if not yet possible with PHP) such things
needs to be considered on producing clean interfaces.
You already provide a lookup mechanism with
MyEnum::from()
- I don't
see a real use-case for proving a pre build map. The main use case I see
is to list all possible enum values but this doesn't require a map and a
zero-indexed-array would also be more performant with packed arrays
(correct me if I'm wrong).
Thanks,
Marc
--Larry Garfield
Hi Marc
I don't have a really good use-case for float values. It just seems
weird to me that a ScalarEnum doesn't support all scalars.Using the enum value as array key for
cases()
works with your current
proposal but if we later want to allow floats, bool whatever then we got
a food gun.
The main reason is that we're using a hashmap internally in from() to
find the given case you're looking for. This is the same hashmap PHP
arrays are based on which only supports ints/strings as keys. If we
were to allow any scalar as a value, looking up a case by value would
become a O(n) operation.
We could do something terrible like serialize the key before storing
it in the hashmap to allow arbitrary key types. But that will require
serializing the value on each invocation of from() which will
unnecessarily slow down the 95% most common use cases (int/string) to
support the exception. Note though that it's always easier to extend
than to remove. By not offering this feature we're erring on the side
of caution.
That being said, I can see how ScalarEnum is a misleading name. We've
been thinking about a better name and only had some ideas we weren't
fully satisfied with. RawEnum, ValueEnum and ConvertibleEnum were some
of these ideas. Let us know if you have a better suggestion.
You already provide a lookup mechanism with
MyEnum::from()
- I don't
see a real use-case for proving a pre build map. The main use case I see
is to list all possible enum values but this doesn't require a map and a
zero-indexed-array would also be more performant with packed arrays
(correct me if I'm wrong).
I do somewhat agree with you there. We're essentially returning
Array<UnitEnum>|Map<int|string, ScalarEnum>
which feels
inconsistent. When you're calling cases() you're most likely going to
loop over it at which point $case->value is available at your
disposal.
Ilija
Hi Ilija,
Hi Marc
I don't have a really good use-case for float values. It just seems
weird to me that a ScalarEnum doesn't support all scalars.Using the enum value as array key for
cases()
works with your current
proposal but if we later want to allow floats, bool whatever then we got
a food gun.
The main reason is that we're using a hashmap internally in from() to
find the given case you're looking for. This is the same hashmap PHP
arrays are based on which only supports ints/strings as keys. If we
were to allow any scalar as a value, looking up a case by value would
become a O(n) operation.wWe could do something terrible like serialize the key before storing
it in the hashmap to allow arbitrary key types. But that will require
serializing the value on each invocation of from() which will
unnecessarily slow down the 95% most common use cases (int/string) to
support the exception. Note though that it's always easier to extend
than to remove. By not offering this feature we're erring on the side
of caution.That being said, I can see how ScalarEnum is a misleading name. We've
been thinking about a better name and only had some ideas we weren't
fully satisfied with. RawEnum, ValueEnum and ConvertibleEnum were some
of these ideas. Let us know if you have a better suggestion.
That's reasonable and makes sense at this point. Only supporting string
and int is also fine (I don't have a personal use case for other types).
So using the same HT implementation as arrays internally totally makes
sense but this is an implementation detail not visible for the outside
and we shouldn't block outself for the future now as nobody knows of
unknown possible use cases. At least if we can avoid it.
You already provide a lookup mechanism with
MyEnum::from()
- I don't
see a real use-case for proving a pre build map. The main use case I see
is to list all possible enum values but this doesn't require a map and a
zero-indexed-array would also be more performant with packed arrays
(correct me if I'm wrong).
I do somewhat agree with you there. We're essentially returning
Array<UnitEnum>|Map<int|string, ScalarEnum>
which feels
inconsistent. When you're calling cases() you're most likely going to
loop over it at which point $case->value is available at your
disposal.
Would you consider making cases()
returning a simple list in all cases
instead of differentiate between UnitEnum and ScalarEnum given the fact
that mostly people just want to loop over cases and a lookup is already
available with ScalarEnum::from() to provide a cleaner interface?
Marc
Ilija
You already provide a lookup mechanism with
MyEnum::from()
- I don't
see a real use-case for proving a pre build map. The main use case I see
is to list all possible enum values but this doesn't require a map and a
zero-indexed-array would also be more performant with packed arrays
(correct me if I'm wrong).
I do somewhat agree with you there. We're essentially returning
Array<UnitEnum>|Map<int|string, ScalarEnum>
which feels
inconsistent. When you're calling cases() you're most likely going to
loop over it at which point $case->value is available at your
disposal.Would you consider making
cases()
returning a simple list in all cases
instead of differentiate between UnitEnum and ScalarEnum given the fact
that mostly people just want to loop over cases and a lookup is already
available with ScalarEnum::from() to provide a cleaner interface?Marc
Ilija and I talked this one over a bit more, and decided that you're right. Between ->value and from() we couldn't come up with a use case that would need the assoc array that wouldn't work just as well with ->value, and it makes the method type definition simpler.
I've updated the RFC to have cases() always return a packed array; Ilija will update the PR soon.
Thanks for your feedback!
--Larry Garfield
Hi,
The full RFC is here, and I recommend reading it again in full given how much was updated.
I tried to answer the following question but failed to do so:
What is the scalar value for a ScalarEnum if none is explicitly defined?
The RFC makes this example:
enum Suit: string implements Colorful {
case Hearts = 'H';
case Diamonds = 'D';
…
'D' == Suit::Diamonds->value; // true
What in this case?
enum Suit: string {
case Hearts;
case Diamonds;
…
What is the outcome of Suit::Diamonds->value
?
Thanks!
- Markus
What is the scalar value for a ScalarEnum if none is explicitly
defined?
The question has no answer, because the declaration of the enum itself would be invalid:
If an enumeration is marked as having a scalar equivalent, then all cases must have a unique scalar equivalent defined explicitly.
enum Suit: string {
case Hearts;
case Diamonds;
}
Presumably, this would result in an Error being thrown when compiling the declaration.
Regards,
--
Rowan Tommins
[IMSoP]
What is the scalar value for a ScalarEnum if none is explicitly
defined?The question has no answer, because the declaration of the enum itself would be invalid:
If an enumeration is marked as having a scalar equivalent, then all cases must have a unique scalar equivalent defined explicitly.
enum Suit: string {
case Hearts;
case Diamonds;
}Presumably, this would result in an Error being thrown when compiling the declaration.
I see.
What's the quickest way (=less code) to have an enum represent it's
lexical name as the literal values?
So that … case Foo; case Bar; …
results in ::Foo->value === 'Foo'
etc.?
Is this possible without implementing this manually for all cases?
thanks,
- Markus
What's the quickest way (=less code) to have an enum represent it's
lexical name as the literal values?So that
… case Foo; case Bar; …
results in::Foo->value === 'Foo'
etc.?Is this possible without implementing this manually for all cases?
AFAICS, you'd need to implement the values manually. The $value property is
only defined on ScalarEnum, not UnitEnum.
- Benjamin
Hi,
The full RFC is here, and I recommend reading it again in full given how much was updated.
I tried to answer the following question but failed to do so:
What is the scalar value for a ScalarEnum if none is explicitly defined?
The RFC makes this example:
enum Suit: string implements Colorful {
case Hearts = 'H';
case Diamonds = 'D';
…
'D' == Suit::Diamonds->value; // trueWhat in this case?
enum Suit: string {
case Hearts;
case Diamonds;
…What is the outcome of
Suit::Diamonds->value
?
A parse error on the declaration; if it's a scalar enum, explicit values are required for all cases.
--Larry Garfield
On Mon, Dec 28, 2020 at 10:22 PM Larry Garfield larry@garfieldtech.com
wrote:
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to offer
you round 2 on enumerations. This is in the spirit of the previous
discussion, but based on that discussion a great deal has been reworked.
The main change is that Enumeration Cases are now object instances of the
Enumeration class rather than their own class. Most of the other changes
are knock-on effects of that.Of particular note:
- Cases may not have methods or constants on them. They're just dumb
values.- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given how
much was updated.https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in
reflection, and some opcache bugs that Ilija is still stomping on.There are a few outstanding questions listed that we would like feedback
on. We're not entirely certain which direction to go with them, for
reasons explained in the RFC. Input on those is especially welcome.Happy New Year. May it be enumerable.
--
Larry Garfield
larry@garfieldtech.com--
To unsubscribe, visit: https://www.php.net/unsub.php
Hi!
Nice evolution overall.
Few notes:
- I think ScalarEnum::fromValue() would be more clear than just from() and
more in sync with ->value property. - For the class structure you gave an example to illustrate the 'similar'
way it is related to class, it would be nice to also mark the class as
final. - I agree with no state behavior, at least at this point. However, I would
have liked to offer the ability for very easy creation of
singleton/multiton patterns using enums, like in Java. - Enums evolve in time and cases are added or sometimes removed. When the
storage of the case stays externally (as scalar values or serialized),
there could be issues when some of them are removed in the meantime.- for ScalarEnum::from(), for missing values I would guess we will throw
an exception, maybe a specific one? Can we mention it in the RFC? - for deserialization, how should it be handled? using an exception as
well, same exception as in the previous case.
- for ScalarEnum::from(), for missing values I would guess we will throw
- The allowed_classes option on
unserialize()
method, I'm guessing it will
work with Enums just like every other class, putting an instance of
__PHP_Incomplete_Class? - Inheritance would work between enums. But only by eliminating some cases.
I can see how enum RedSuites could extend enum Suites without breaking LSP.
Not sure if it makes sense to dig on this now.
Regards,
Alex
Nice evolution overall.
Few notes:
- I think ScalarEnum::fromValue() would be more clear than just from() and
more in sync with ->value property.
Any name would work, I suppose. I prefer short, single-word methods where possible, myself.
- For the class structure you gave an example to illustrate the 'similar'
way it is related to class, it would be nice to also mark the class as
final.
It would. Added.
- I agree with no state behavior, at least at this point. However, I would
have liked to offer the ability for very easy creation of
singleton/multiton patterns using enums, like in Java.
I'm not sure what you're describing here. Do you mean tagged unions/ADTs, which are punted to a later RFC?
https://wiki.php.net/rfc/tagged_unions
- Enums evolve in time and cases are added or sometimes removed. When the
storage of the case stays externally (as scalar values or serialized),
there could be issues when some of them are removed in the meantime.
Possibly; though that's no different than a class evolving or going away in time. Either way it's up to the developer to figure out what BC strategy makes sense.
- for ScalarEnum::from(), for missing values I would guess we will throw
an exception, maybe a specific one? Can we mention it in the RFC?
It already says a ValueError is thrown. It probably does make sense to subclass that, though. I'll mention it to Ilija.
- for deserialization, how should it be handled? using an exception as
well, same exception as in the previous case.
Ilija and I discussed this a bit, and settled on an invalid deserialiation issuing a warning and returning false. An exception would probably be better, but right now deserialize() doesn't throw things so introducing that just for enums seems inconsistent. He'll be updating the implementation shortly.
- The allowed_classes option on
unserialize()
method, I'm guessing it will
work with Enums just like every other class, putting an instance of
__PHP_Incomplete_Class?
Enums don't have unsafe behavior in their constructors the way classes do, so we're not convinced there's a reason to exclude them. For now allowed_classes does not include enums.
- Inheritance would work between enums. But only by eliminating some cases.
I can see how enum RedSuites could extend enum Suites without breaking LSP.
Not sure if it makes sense to dig on this now.Regards,
Alex
Possibly as a future scope, if we see demand for it. The RFC is ambitious enough as is and already has several follow-ups planned for more advanced cases. :-)
Cheers!
--Larry Garfield
After considerable discussion and effort, Ilija and I are ready to offer you round 2 on enumerations.
Thank you both, again, for all your efforts. I'm pleased to say that I
like this draft even more than the last one. :)
A couple of points that occurred to me reading through:
-
The magic methods section lists __call as allowed, but not
__callStatic; was this deliberate, or just an oversight? -
Under Future Scope, the "Grouped Syntax" sub-section says "That would
only work on the simple, non-primitive-backed case with no methods
defined [...] it is unclear how common that will be in practice" This
caveat doesn't apply to the current proposed syntax, and should perhaps
be re-visited.
Given that this is currently a legal declaration:
class Suit {
const Hearts = 'H', Diamonds = 'D', Clubs = 'C', Spades = 'S';
}
It seems fairly reasonable for the enum version to allow the same syntax:
enum Suit: string {
case Hearts = 'H', Diamonds = 'D', Clubs = 'C', Spades = 'S';
}
Or, for a non-scalar enum:
enum Suit {
case Hearts, Diamonds, Clubs, Spades;
}
Regards,
--
Rowan Tommins
[IMSoP]
After considerable discussion and effort, Ilija and I are ready to offer you round 2 on enumerations.
Thank you both, again, for all your efforts. I'm pleased to say that I
like this draft even more than the last one. :)A couple of points that occurred to me reading through:
- The magic methods section lists __call as allowed, but not
__callStatic; was this deliberate, or just an oversight?
More oversight; the reasons for not including it initially aren't really good reasons, so we'll unblock that.
- Under Future Scope, the "Grouped Syntax" sub-section says "That would
only work on the simple, non-primitive-backed case with no methods
defined [...] it is unclear how common that will be in practice" This
caveat doesn't apply to the current proposed syntax, and should perhaps
be re-visited.Given that this is currently a legal declaration:
class Suit {
const Hearts = 'H', Diamonds = 'D', Clubs = 'C', Spades = 'S';
}It seems fairly reasonable for the enum version to allow the same syntax:
enum Suit: string {
case Hearts = 'H', Diamonds = 'D', Clubs = 'C', Spades = 'S';
}Or, for a non-scalar enum:
enum Suit {
case Hearts, Diamonds, Clubs, Spades;
}
That's partially left over from when we had per-case methods, where grouping would be highly fugly. However, I'm still advocating for tagged unions (in a future step) having per-case methods, and that would make the grouping syntax fugly again. It's trivial to add later, so for now I think it's best to skip. If we find later on it doesn't get in the way of anything it's an easy addition.
--Larry Garfield
That's partially left over from when we had per-case methods, where grouping would be highly fugly. However, I'm still advocating for tagged unions (in a future step) having per-case methods, and that would make the grouping syntax fugly again. It's trivial to add later, so for now I think it's best to skip. If we find later on it doesn't get in the way of anything it's an easy addition.
Yes, that had occurred to me. Just to reiterate, though, the original
justification read "it is unclear how common [cases without block
definitions] will be in practice"; with the current implementation, we
know for a fact that it will be extremely common - even if tagged unions
use overlapping syntax that doesn't group nicely, every "unit enum" will
have cases with no blocks.
Still, it's not a big deal either way, and as you say can easily be
added later.
Regards,
--
Rowan Tommins
[IMSoP]
https://wiki.php.net/rfc/enumerations
Why can't this be simplified to:
enum Size {
case Small;
case Medium;
case Large;
}
'Small' === Size::Small->value; // true
Size::from('Small') === Size::Small; // true
enum Suit {
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
'H' === Suit::Hearts->value; // true
'Hearts' === Suit::Hearts->value; // false
Also, how about using a method instead of property for ->value?
--
Aleksander Machniak
Kolab Groupware Developer [https://kolab.org]
Roundcube Webmail Developer [https://roundcube.net]
PGP: 19359DC1 # Blog: https://kolabian.wordpress.com
https://wiki.php.net/rfc/enumerations
Why can't this be simplified to:enum Size {
case Small;
case Medium;
case Large;
}'Small' === Size::Small->value; // true
Size::from('Small') === Size::Small; // trueenum Suit {
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}'H' === Suit::Hearts->value; // true
'Hearts' === Suit::Hearts->value; // false
That's a possibility we've been kicking around. I'm considering it, Ilija doesn't care for it. :-) What's your argument in favor?
Related: There is an internal "case" value that is used as well. It could be exposed as a normal read-only property, which would always be equal to the name of the case ("Hearts"), regardless of whether another "value" is defined. We're still undecided on that, too.
Also, how about using a method instead of property for ->value?
We originally did. Ilija preferred moving it to a property since there will need to be one anyway to store the value. I moderately favor a method but don't greatly care either way.
--Larry Garfield
enum Suit {
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}'H' === Suit::Hearts->value; // true
'Hearts' === Suit::Hearts->value; // falseThat's a possibility we've been kicking around. I'm considering it, Ilija doesn't care for it. :-) What's your argument in favor?
My argument is that, from an end-user perspective, I don't really see
why Unit and Scalar enums have to have different "API" at this point.
I'm talking about ":string"/":int" in the enum definiton as well as
->value and ->from().
--
Aleksander Machniak
Kolab Groupware Developer [https://kolab.org]
Roundcube Webmail Developer [https://roundcube.net]
PGP: 19359DC1 # Blog: https://kolabian.wordpress.com
My argument is that, from an end-user perspective, I don't really see
why Unit and Scalar enums have to have different "API" at this point.
I'm talking about ":string"/":int" in the enum definiton as well as
->value and ->from().
My personal opinion is that for many enums, explicitly not having a
scalar representation is a good thing.
This is basically similar to my opinion of __toString() etc: if you have
multiple ways to convert something to/from a scalar, blessing one of
them as "default" is arbitrary and confusing.
For example:
enum BookingStatus {
case PENDING;
case CONFIRMED;
case CANCELLED;
public function getId() {
return match($this) {
self::PENDING => 1,
self::CONFIRMED => 2,
self::CANCELLED => 3,
};
}
public function getCode() {
return match($this) {
self::PENDING => 'PEN',
self::CONFIRMED => 'CON',
self::CANCELLED => 'CAN',
};
}
public function getEnglishDescription() {
return match($this) {
self::PENDING => 'Pending Payment',
self::CONFIRMED => 'Confirmed',
self::CANCELLED => 'Cancelled',
};
}
}
Regards,
--
Rowan Tommins
[IMSoP]
My argument is that, from an end-user perspective, I don't really see
why Unit and Scalar enums have to have different "API" at this point.
I'm talking about ":string"/":int" in the enum definiton as well as
->value and ->from().My personal opinion is that for many enums, explicitly not having a
scalar representation is a good thing.This is basically similar to my opinion of __toString() etc: if you have
multiple ways to convert something to/from a scalar, blessing one of
them as "default" is arbitrary and confusing.For example:
enum BookingStatus {
case PENDING;
case CONFIRMED;
case CANCELLED;public function getId() {
return match($this) {
self::PENDING => 1,
self::CONFIRMED => 2,
self::CANCELLED => 3,
};
}
public function getCode() {
return match($this) {
self::PENDING => 'PEN',
self::CONFIRMED => 'CON',
self::CANCELLED => 'CAN',
};
}
public function getEnglishDescription() {
return match($this) {
self::PENDING => 'Pending Payment',
self::CONFIRMED => 'Confirmed',
self::CANCELLED => 'Cancelled',
};
}
}
That is similar to our reasoning. It creates a foot-gun situation where someone could get in the habit of assuming that an enum always has a reasonable and logical and thus reliable string equivalent, when not all enums will have string equivalents that it's reasonable and logical to use. So, one less foot gun.
Also, one of the extensions planned, as noted, is ADTs/tagged unions. Those could not have a primitive equivalent, since they're not singletons. Keeping UnitEnum and ScalarEnum separate allows us to later add TaggedEnum (or similar) that also extends UnitEnum, but not ScalarEnum.
--Larry Garfield
My argument is that, from an end-user perspective, I don't really see
why Unit and Scalar enums have to have different "API" at this point.
I'm talking about ":string"/":int" in the enum definiton as well as
->value and ->from().My personal opinion is that for many enums, explicitly not having a
scalar representation is a good thing.This is basically similar to my opinion of __toString() etc: if you have
multiple ways to convert something to/from a scalar, blessing one of
them as "default" is arbitrary and confusing.For example:
enum BookingStatus {
case PENDING;
case CONFIRMED;
case CANCELLED;public function getId() { return match($this) { self::PENDING => 1, self::CONFIRMED => 2, self::CANCELLED => 3, }; } public function getCode() { return match($this) { self::PENDING => 'PEN', self::CONFIRMED => 'CON', self::CANCELLED => 'CAN', }; } public function getEnglishDescription() { return match($this) { self::PENDING => 'Pending Payment', self::CONFIRMED => 'Confirmed', self::CANCELLED => 'Cancelled', }; }
}
That is similar to our reasoning. It creates a foot-gun situation where someone could get in the habit of assuming that an enum always has a reasonable and logical and thus reliable string equivalent, when not all enums will have string equivalents that it's reasonable and logical to use. So, one less foot gun.
However, avoiding one foot-gun does not always mean that you avoid all foot-guns.
For example, when you need to create a large number of string enums where the name and the symbol are the same, it would be very easy to have a typo in one but not the other, especially as a result of copy and paste editing.
So in my perfect world this:
enum BookingStatus {
case PENDING;
case CONFIRMED;
case CANCELLED;
}
Would be equivalent to:
enum BookingStatus {
case PENDING = "PENDING";
case CONFIRMED = "CONFIRMED";
case CANCELLED = "CANCELLED";
}
#fwiw
Also, one of the extensions planned, as noted, is ADTs/tagged unions. Those could not have a primitive equivalent, since they're not singletons. Keeping UnitEnum and ScalarEnum separate allows us to later add TaggedEnum (or similar) that also extends UnitEnum, but not ScalarEnum.
--Larry Garfield
--
To unsubscribe, visit: https://www.php.net/unsub.php
Hi,
So in my perfect world this:
enum BookingStatus {
case PENDING;
case CONFIRMED;
case CANCELLED;
}Would be equivalent to:
enum BookingStatus {
case PENDING = "PENDING";
case CONFIRMED = "CONFIRMED";
case CANCELLED = "CANCELLED";
}
I'm with Mikes' suggesting here, see also my previous messages [1] [2].
I don't know how to back this up with numbers, but the way I see it the
majority of use cases will have a benefit of being able to directly use
the literal values derived from the lexical ones and the ability to have
custom values is feature next to it.
[1] https://externals.io/message/112626#112655
[2] https://externals.io/message/112626#112663
So in my perfect world this:
enum BookingStatus {
case PENDING;
case CONFIRMED;
case CANCELLED;
}Would be equivalent to:
enum BookingStatus {
case PENDING = "PENDING";
case CONFIRMED = "CONFIRMED";
case CANCELLED = "CANCELLED";
}I'm with Mikes' suggesting here, see also my previous messages [1] [2].
I don't know how to back this up with numbers, but the way I see it
the majority of use cases will have a benefit of being able to
directly use the literal values derived from the lexical ones and the
ability to have custom values is feature next to it.
I would personally be OK with this if it was allowed but opt-in, e.g.
adding the ": string" would default the values in that way, or even
something magic like ": auto".
I'm still not a fan of having values always defined, because I want to
be able to pick case labels without worrying about whether they make
sense as values.
For instance, there's a similar maintenance issue to named parameters:
having a default value means if I write "case PENDING;" in v1.0, I can't
change that to "case PENDING='P';" or "case PENDING=1;" in v1.1, in case
somebody is relying on the value being 'PENDING', which was never intended.
Regards,
--
Rowan Tommins
[IMSoP]
So in my perfect world this:
enum BookingStatus {
case PENDING;
case CONFIRMED;
case CANCELLED;
}Would be equivalent to:
enum BookingStatus {
case PENDING = "PENDING";
case CONFIRMED = "CONFIRMED";
case CANCELLED = "CANCELLED";
}I'm with Mikes' suggesting here, see also my previous messages [1] [2].
I don't know how to back this up with numbers, but the way I see it the majority of use cases will have a benefit of being able to directly use the literal values derived from the lexical ones and the ability to have custom values is feature next to it.
I would personally be OK with this if it was allowed but opt-in, e.g. adding the ": string" would default the values in that way, or even something magic like ": auto".
I'm still not a fan of having values always defined, because I want to be able to pick case labels without worrying about whether they make sense as values.
For instance, there's a similar maintenance issue to named parameters: having a default value means if I write "case PENDING;" in v1.0, I can't change that to "case PENDING='P';" or "case PENDING=1;" in v1.1, in case somebody is relying on the value being 'PENDING', which was never intended.
You make a good point about being able to define without defaults, and for being opt-in instead.
A use-case I am thinking about is a long list of application-specific error codes where you don't want to have to come up two values for each error code and/or worry about keeping them the same.
I don't quit get how you are thinking of with ": string"; can you give an example?
Conversely, (something like) this might be a viable way to opt-in default values:
enum defaults BookingStatus {
case PENDING;
case CONFIRMED;
case CANCELLED;
}
-Mike
I don't quit get how you are thinking of with ": string"; can you give an example?
The current RFC requires the declaration of a Scalar Enum [1] to include
the scalar type, so it looks like this:
enum BookingStatus: string {
case PENDING = "PENDING";
case CONFIRMED = "CONFIRMED";
case CANCELLED = "CANCELLED";
}
The simplest opt-in for default scalar values would be to declare the
type but no values, so the above could be abbreviated this way:
enum BookingStatus: string {
case PENDING;
case CONFIRMED;
case CANCELLED;
}
However, it's probably better to be a bit more explicit, e.g.:
enum BookingStatus: auto string {
case PENDING;
case CONFIRMED;
case CANCELLED;
}
Either way, we'd have to decide if it was allowed to specify some values
but not all, e.g.:
enum BookingStatus: auto string {
case PENDING;
case CONFIRMED;
case CANCELLED = 'TERMINATED';
}
A more verbose but possibly clearer syntax would be a token on the case
statements themselves, such as "= auto", to mean "set the value for this
case based on its constant name":
enum BookingStatus: string {
case PENDING = auto;
case CONFIRMED = auto;
case CANCELLED = 'TERMINATED';
}
There's also the question of whether an automatic value for ints should
also be allowed, e.g. incrementing in declaration order. This is more
dangerous, as adding, removing, or re-arranging cases can affect the
values of all the cases in the enum, but is done in some other languages.
[1] https://wiki.php.net/rfc/enumerations#scalar_enums
Regards,
--
Rowan Tommins
[IMSoP]
Hi Larry,
pon., 28 gru 2020 o 21:22 Larry Garfield larry@garfieldtech.com
napisał(a):
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to offer
you round 2 on enumerations. This is in the spirit of the previous
discussion, but based on that discussion a great deal has been reworked.
The main change is that Enumeration Cases are now object instances of the
Enumeration class rather than their own class. Most of the other changes
are knock-on effects of that.Of particular note:
- Cases may not have methods or constants on them. They're just dumb
values.- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given how
much was updated.https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in
reflection, and some opcache bugs that Ilija is still stomping on.
I really like the shape of the current RFC.
I'd like to ask a few things:
- Regarding the Scalar Enums since scalar values need to be literal and
by design they're read only why they use a spear "->value" on enumeration?
A spear "->" in objects is dedicated to class properties and by default
they make thinking of property which
in most cases is read-write visible.
An example using Suit enumeration from RFC:
// when I assign an enumeration to variable
$var = Suit::Spades;
// then reaching it's value using "->value"
echo $var->value; // 'C'
// Looks just like an object property fetch
By default if a property is visible it is write accessible as well, which
may confuse.
Instead of using spear "->value" would it be possible to fetch the value
just like object constants?
print Suit::Clubs::value; // 'C'
echo $var::value; // 'C'
This mentally indicates by default that it's value is constant, simple
scalar value and read only!
- Regarding the Scalar Enums declaration syntax
enum Suit: string {
case Hearts = 'H';
}
Would it make sense to move the part declaring enumeration value before
enum name?
I think it was put here to look similar to function return type, but IMHO
it looks better and reads easier
when moved before enum name:
enum:string Suit {
case Hearts = 'H';
}
Leaving the space between enum name and bracket for further extensions in
future.
What do you think?
Cheers,
Michał Marcin Brzuchalski
Hi Larry,
I really like the shape of the current RFC.
I'd like to ask a few things:
- Regarding the Scalar Enums since scalar values need to be literal and
by design they're read only why they use a spear "->value" on enumeration?
A spear "->" in objects is dedicated to class properties and by default
they make thinking of property which
in most cases is read-write visible.An example using Suit enumeration from RFC:
// when I assign an enumeration to variable
$var = Suit::Spades;
// then reaching it's value using "->value"
echo $var->value; // 'C'
// Looks just like an object property fetchBy default if a property is visible it is write accessible as well, which
may confuse.Instead of using spear "->value" would it be possible to fetch the value
just like object constants?print Suit::Clubs::value; // 'C'
echo $var::value; // 'C'This mentally indicates by default that it's value is constant, simple
scalar value and read only!
We originally used a method in the first draft. We changed it to a property for three reasons.
- There's lots of talk lately about immutable properties, so one way or another "it's a property so I can write to it" is an assumption that won't hold much longer.
- The property has to exist internally anyway, because under the hood it's just a class with a property, so the name would still be reserved either way.
- Given 1 and 2, adding a method on top just adds some micro overhead for the method call.
A constant-esque syntax wouldn't be possible because under the hood, the cases are objects now, not classes. We could put constants on Suit, but Suit::Clubs is an object, and objects can't have constants independent of their class.
- Regarding the Scalar Enums declaration syntax
enum Suit: string {
case Hearts = 'H';
}Would it make sense to move the part declaring enumeration value before
enum name?
I think it was put here to look similar to function return type, but IMHO
it looks better and reads easier
when moved before enum name:enum:string Suit {
case Hearts = 'H';
}Leaving the space between enum name and bracket for further extensions in
future.
What do you think?
That would be a very one-off syntax. The colon-suffix is already established in PHP, and i the same as the syntax used in Swift. What you're describing looks a lot more like a generic. Which... I suppose in some ways this is if you squint really hard? I don't have much of an argument against it other than aesthetic. I have no idea what the parser would do with it.
--Larry Garfield
On Mon, Dec 28, 2020 at 9:22 PM Larry Garfield larry@garfieldtech.com
wrote:
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to offer
you round 2 on enumerations. This is in the spirit of the previous
discussion, but based on that discussion a great deal has been reworked.
The main change is that Enumeration Cases are now object instances of the
Enumeration class rather than their own class. Most of the other changes
are knock-on effects of that.Of particular note:
- Cases may not have methods or constants on them. They're just dumb
values.- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given how
much was updated.https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in
reflection, and some opcache bugs that Ilija is still stomping on.There are a few outstanding questions listed that we would like feedback
on. We're not entirely certain which direction to go with them, for
reasons explained in the RFC. Input on those is especially welcome.Happy New Year. May it be enumerable.
I think the reflection part is the weakest of this proposal, in my opinion
there should not be a ReflectionEnum and ReflectionCase.
- ReflectionEnum extends ReflectionClass is problematic
- is hasEnum just an alias for hasConstant?
- Same for getCases() for getConstants().
- what does getConstant() or ReflectionClassConstant::getValue return for a
non scalar enum? the instance?
In my opinion you should embrace the "desugarizing" like constructor
promotion and just keep everything with the exsiting Reflection structure.
--
Larry Garfield
larry@garfieldtech.com--
To unsubscribe, visit: https://www.php.net/unsub.php
On Mon, Dec 28, 2020 at 9:22 PM Larry Garfield larry@garfieldtech.com
wrote:Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to offer
you round 2 on enumerations. This is in the spirit of the previous
discussion, but based on that discussion a great deal has been reworked.
The main change is that Enumeration Cases are now object instances of the
Enumeration class rather than their own class. Most of the other changes
are knock-on effects of that.Of particular note:
- Cases may not have methods or constants on them. They're just dumb
values.- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given how
much was updated.https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in
reflection, and some opcache bugs that Ilija is still stomping on.There are a few outstanding questions listed that we would like feedback
on. We're not entirely certain which direction to go with them, for
reasons explained in the RFC. Input on those is especially welcome.Happy New Year. May it be enumerable.
I think the reflection part is the weakest of this proposal, in my opinion
there should not be a ReflectionEnum and ReflectionCase.
I'd agree that Reflection is the place we still have the most open questions. (The listed attribute open questions are synonyms for reflection questions, in a sense.) I disagree with removing their reflection classes entirely, though.
- ReflectionEnum extends ReflectionClass is problematic
Why? The main alternative is making it its own thing entirely, which seemed redundant.
- is hasEnum just an alias for hasConstant?
Assuming you mean hasCase(), no. An enum can have constants now in addition to cases. So for instance:
enum Foo {
case Bar;
const Beep = self::Bar;
}
hasCase('Beep') would return false. hasConstant('Beep') returns true.
- Same for getCases() for getConstants().
As above. Not all constants are cases. That cases are constants is an implementation convenience.
- what does getConstant() or ReflectionClassConstant::getValue return for a
non scalar enum? the instance?
ReflectionEnum::getConstant() would return the case instance object, whether or not it's scalar.
In my opinion you should embrace the "desugarizing" like constructor
promotion and just keep everything with the exsiting Reflection structure.
Enums are more than "just" sugar on objects, though. They're built on objects, but they have limitations that objects don't have, and features objects don't have. And ideally those will expand in future RFCs. Having no difference at all between them an objects in reflection even though their functionality is different at a user level seems needlessly confusing and limiting to me.
--Larry Garfield
On Mon, Dec 28, 2020 at 9:22 PM Larry Garfield larry@garfieldtech.com
wrote:
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to offer
you round 2 on enumerations. This is in the spirit of the previous
discussion, but based on that discussion a great deal has been reworked.
The main change is that Enumeration Cases are now object instances of the
Enumeration class rather than their own class. Most of the other changes
are knock-on effects of that.Of particular note:
- Cases may not have methods or constants on them. They're just dumb
values.- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given how
much was updated.https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in
reflection, and some opcache bugs that Ilija is still stomping on.There are a few outstanding questions listed that we would like feedback
on. We're not entirely certain which direction to go with them, for
reasons explained in the RFC. Input on those is especially welcome.Happy New Year. May it be enumerable.
Nice work, I like the updated proposal. Some notes:
Similarly, enum names and case names are both case insensitive.
I agree that enum names should be case insensitive (like class names), but
why should case names be case insensitive? The closest analogon to a case
would be a class constant, and those are case sensitive.
All Cases have a read-only property, case, that is the case-sensitive
name of the case itself.
I can see how this makes sense, but it wouldn't be my first guess as to how
you access the case name. I'm wondering if using $enumValue->name or
$enumValue->caseName might be preferable.
Scalar equivalent values must be literals. Constants and constant
expressions are not supported.
Why? This seems inconsistent with the overall language. If I can use a
constant expression as a class constant value, why can't I use it as an
enum value?
ScalarEnum exposes an additional static method from() that is
automatically generated.
I think it would be good to be slightly more explicit here and call it
fromValue(). (We could have fromName() to construct it from a case name,
and any number of custom from* named constructors.)
The from() method will up-cast from a scalar to its corresponding
Enumerated Case. Invalid scalars with no matching Case will throw a
ValueError. There is also a has() method, which will return boolean true if
a case with that value exists and false otherwise
Just a thought, but rather than having has() and from(), it may make sense
to have from() (throws if invalid) and tryFrom() (returns null if invalid).
I think a has() method is pretty much useless in isolation, it will always
be used in a combination of has() and from(), in which case it is better to
combine them rather than have an implicit contract between them.
Manually defining a static from() or has() method on a Scalar Enum will
result in a fatal error.
Or a non-static one :) You can't define a static and a non-static method
with the same name in PHP.
Enums and cases may have attributes attached to them, like any other
language construct. The TARGET_CLASS target filter will include Enums
themselves. The TARGET_CONST target filter will include Enum Cases
TARGET_CONST should presumably be TARGET_CLASS_CONST. More generally
though, I wonder if there should be a separate TARGET_CASE...
Returns the scalar equivalent type of the Enum, if any. If it doesn't
have one, it returns a ReflectionType on null.
Possibly I'm misunderstanding the sentence, but why does it return a
"ReflectionType on null" rather than just "null"? It's not like ::$value
will have a null value in this case, it will not exist at all.
Additionally, a new function is_enum(mixed): bool returns true if the
value passed is an enum or case object.
Could you please clarify what "an enum or case object" means? Is this
function both for checking whether an object is a case object and a
(string) class an enum class?
Some typo/etc notes on the text:
All Unit Enums as implemented as instances of their enum type.
"as" -> "are" possibly?
That is one way to determine an Enum from any other object:
"determine" -> "distinguish" possibly?
The above hierarchy is logically similar to the following class structure
(although this is not the actual code that runs):
You might want to change the constructor to
private function __construct(public string $case) {}
in this example. The use of "new static" for a final class is also
confusing.
Regards,
Nikita
Nice work, I like the updated proposal. Some notes:
Similarly, enum names and case names are both case insensitive.
I agree that enum names should be case insensitive (like class names), but
why should case names be case insensitive? The closest analogon to a case
would be a class constant, and those are case sensitive.
This is a left over from the earlier draft when Cases were also classes. I've corrected it. Thanks for the catch.
All Cases have a read-only property, case, that is the case-sensitive
name of the case itself.I can see how this makes sense, but it wouldn't be my first guess as to how
you access the case name. I'm wondering if using $enumValue->name or
$enumValue->caseName might be preferable.
We'll change it to ->name.
Scalar equivalent values must be literals. Constants and constant
expressions are not supported.Why? This seems inconsistent with the overall language. If I can use a
constant expression as a class constant value, why can't I use it as an
enum value?
Mostly because it was more challenging to do. We're fine with including it if Ilija can make it work. (Suggestions for him on how to do so most efficiently are welcome.)
ScalarEnum exposes an additional static method from() that is
automatically generated.I think it would be good to be slightly more explicit here and call it
fromValue(). (We could have fromName() to construct it from a case name,
and any number of custom from* named constructors.)The from() method will up-cast from a scalar to its corresponding
Enumerated Case. Invalid scalars with no matching Case will throw a
ValueError. There is also a has() method, which will return boolean true if
a case with that value exists and false otherwiseJust a thought, but rather than having has() and from(), it may make sense
to have from() (throws if invalid) and tryFrom() (returns null if invalid).
I think a has() method is pretty much useless in isolation, it will always
be used in a combination of has() and from(), in which case it is better to
combine them rather than have an implicit contract between them.
Hm, maybe. My concern is that "try" to me implies "exceptions are involved somewhere", since PHP doesn't have the "TryX means nullable return" convention that Rust or C# have.
Anyone else want to weigh in?
Manually defining a static from() or has() method on a Scalar Enum will
result in a fatal error.Or a non-static one :) You can't define a static and a non-static method
with the same name in PHP.
Fixed.
Enums and cases may have attributes attached to them, like any other
language construct. The TARGET_CLASS target filter will include Enums
themselves. The TARGET_CONST target filter will include Enum CasesTARGET_CONST should presumably be TARGET_CLASS_CONST. More generally
though, I wonder if there should be a separate TARGET_CASE...
We had that in the spec until like 40 hours ago. :-) We removed it mainly because beberlei was pushing back on it. (And the corresponding reflection classes, which are still under discussion.)
Personally I'd rather have the flexibility of targeting enums and cases specifically. Ilija and Benjamin feel it's better to "embrace the object-y-ness" of enums and just use the existing targets. It's not a hill I'm going to die on either way, so if you can convince them I'm happy to add them back in.
Returns the scalar equivalent type of the Enum, if any. If it doesn't
have one, it returns a ReflectionType on null.Possibly I'm misunderstanding the sentence, but why does it return a
"ReflectionType on null" rather than just "null"? It's not like ::$value
will have a null value in this case, it will not exist at all.
More likely I'm misunderstanding the nuances of the reflection API. The ideal here is to avoid a nullable return if at all possible. As noted above reflection is the shakiest part left right now, so if you have specific suggestions for how it would work better, please share.
Additionally, a new function is_enum(mixed): bool returns true if the
value passed is an enum or case object.Could you please clarify what "an enum or case object" means? Is this
function both for checking whether an object is a case object and a
(string) class an enum class?
It would return true if given an enum case, a variable that is assigned to an enum case, or a class name string that refers to a class that is an enum. Other than replicating the test for it I;m not sure how better to describe it. (Or, just use that sentence?)
Some typo/etc notes on the text:
All Unit Enums as implemented as instances of their enum type.
"as" -> "are" possibly?
That is one way to determine an Enum from any other object:
"determine" -> "distinguish" possibly?
The above hierarchy is logically similar to the following class structure
(although this is not the actual code that runs):You might want to change the constructor to
private function __construct(public string $case) {}
in this example. The use of "new static" for a final class is also
confusing.
Got them all, thanks. (new static is because at this point I basically never use self and always use static, as it's what I actually want 99.4% of the time.)
--Larry Garfield
On Tue, Jan 5, 2021 at 6:56 PM Larry Garfield larry@garfieldtech.com
wrote:
Nice work, I like the updated proposal. Some notes:
Similarly, enum names and case names are both case insensitive.
I agree that enum names should be case insensitive (like class names),
but
why should case names be case insensitive? The closest analogon to a case
would be a class constant, and those are case sensitive.This is a left over from the earlier draft when Cases were also classes.
I've corrected it. Thanks for the catch.All Cases have a read-only property, case, that is the case-sensitive
name of the case itself.I can see how this makes sense, but it wouldn't be my first guess as to
how
you access the case name. I'm wondering if using $enumValue->name or
$enumValue->caseName might be preferable.We'll change it to ->name.
Scalar equivalent values must be literals. Constants and constant
expressions are not supported.Why? This seems inconsistent with the overall language. If I can use a
constant expression as a class constant value, why can't I use it as an
enum value?Mostly because it was more challenging to do. We're fine with including
it if Ilija can make it work. (Suggestions for him on how to do so most
efficiently are welcome.)
It may be okay to retain the limitation, but in that case the RFC should
include some technical justification for that. It would also be good to
explicitly state whether something like "1 + 1" is allowed, i.e. whether
constant expressions are forbidden entirely, or only those that cannot be
evaluated at compile-time.
ScalarEnum exposes an additional static method from() that is
automatically generated.I think it would be good to be slightly more explicit here and call it
fromValue(). (We could have fromName() to construct it from a case name,
and any number of custom from* named constructors.)The from() method will up-cast from a scalar to its corresponding
Enumerated Case. Invalid scalars with no matching Case will throw a
ValueError. There is also a has() method, which will return boolean true
if
a case with that value exists and false otherwiseJust a thought, but rather than having has() and from(), it may make
sense
to have from() (throws if invalid) and tryFrom() (returns null if
invalid).
I think a has() method is pretty much useless in isolation, it will
always
be used in a combination of has() and from(), in which case it is better
to
combine them rather than have an implicit contract between them.Hm, maybe. My concern is that "try" to me implies "exceptions are
involved somewhere", since PHP doesn't have the "TryX means nullable
return" convention that Rust or C# have.Anyone else want to weigh in?
Manually defining a static from() or has() method on a Scalar Enum will
result in a fatal error.Or a non-static one :) You can't define a static and a non-static method
with the same name in PHP.Fixed.
Enums and cases may have attributes attached to them, like any other
language construct. The TARGET_CLASS target filter will include Enums
themselves. The TARGET_CONST target filter will include Enum CasesTARGET_CONST should presumably be TARGET_CLASS_CONST. More generally
though, I wonder if there should be a separate TARGET_CASE...We had that in the spec until like 40 hours ago. :-) We removed it mainly
because beberlei was pushing back on it. (And the corresponding reflection
classes, which are still under discussion.)Personally I'd rather have the flexibility of targeting enums and cases
specifically. Ilija and Benjamin feel it's better to "embrace the
object-y-ness" of enums and just use the existing targets. It's not a hill
I'm going to die on either way, so if you can convince them I'm happy to
add them back in.
Can't say I have a particularly strong argument either way here :)
Returns the scalar equivalent type of the Enum, if any. If it doesn't
have one, it returns a ReflectionType on null.Possibly I'm misunderstanding the sentence, but why does it return a
"ReflectionType on null" rather than just "null"? It's not like ::$value
will have a null value in this case, it will not exist at all.More likely I'm misunderstanding the nuances of the reflection API. The
ideal here is to avoid a nullable return if at all possible. As noted
above reflection is the shakiest part left right now, so if you have
specific suggestions for how it would work better, please share.
Well, why do you want to avoid a nullable return here? We have many other
getType/getReturnType style APIs that all return null if no type is
specified, so I would expect this one to work the same way. Returning
ReflectionType("null") is disingenuous, as the enum was neither declared as
"enum Foo: null {}", nor does it behave as if it were declared as such.
Additionally, a new function is_enum(mixed): bool returns true if the
value passed is an enum or case object.Could you please clarify what "an enum or case object" means? Is this
function both for checking whether an object is a case object and a
(string) class an enum class?It would return true if given an enum case, a variable that is assigned to
an enum case, or a class name string that refers to a class that is an
enum. Other than replicating the test for it I;m not sure how better to
describe it. (Or, just use that sentence?)
Including an example would be good. I just checked the implementation, and
apparently the actual signature is "is_enum(mixed $value, bool $autoload =
true): bool", which brings me to the concern I have about this API: Mixing
checking for classes, which requires autoloading, and for other values is
generally a bad idea -- been there, done that.
The problem is that if used to check whether a value is an enum instance,
then just naively writing is_enum($value) may trigger autoloading when
$value happens to be a string. This is not just inefficient, but also a
possible security issue. The $autoload flag can mitigate that, but that's
just a workaround for bad function design.
This could be addressed in different ways, e.g. one could have separate
is_enum_class() and is_enum_value() functions, though the first one would
probably more typically be called enum_exists(). I'm not sure if that is
the right way to address it though, as the intended use case for the
is_enum() function is not entirely clear to me.
Regards,
Nikita
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to
offer you round 2 on enumerations. This is in the spirit of the
previous discussion, but based on that discussion a great deal has been
reworked. The main change is that Enumeration Cases are now object
instances of the Enumeration class rather than their own class. Most
of the other changes are knock-on effects of that.Of particular note:
- Cases may not have methods or constants on them. They're just dumb values.
- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given
how much was updated.https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in
reflection, and some opcache bugs that Ilija is still stomping on.There are a few outstanding questions listed that we would like
feedback on. We're not entirely certain which direction to go with
them, for reasons explained in the RFC. Input on those is especially
welcome.Happy New Year. May it be enumerable.
For those who would like to play the home game but haven't been able to, Tyson Andre has build a delightful Wasm version of the enums branch so you can run actual code using enums.
https://tysonandre.github.io/php-rfc-demo/enums/
100% credit to Tyson for putting that together. It's quite slick. :-) (It's a few commits behind but nothing that should impact most testing; just some edge formatting around print_r, reflection, and stuff like that.)
Also, while we were at it, we went with the from()/tryFrom() method pair. No one seemed morally opposed to them and there wasn't really a better naming convention.
Aside from some nitpicking around reflection and possibly fiddling with the naming of "Scalar Enum" et al, we're closing in on the final version. It will probably go to a vote in the not too distant future.
--Larry Garfield
Aside from some nitpicking around reflection and possibly fiddling with
the naming of "Scalar Enum" et al, we're closing in on the final version.
It will probably go to a vote in the not too distant future.--Larry Garfield
--
To unsubscribe, visit: https://www.php.net/unsub.php
Most of this has already been communicated off list but I ought to make my
opinions on the proposal somewhat "official".
Although I do think we need enums backed into PHP, I'm really not keen on
piggy backing on objects.
I'm also aware that this would be a larger undertaking as using objects as
one would need to rewrite part of the logic objects use.
However, looking at how much of the objectness needs to be blocked and/or
modified (traits, final, no properties, etc.), IMHO, points towards a new
zval structure instead of reusing objects, at least for the simple cases as
a simple enumeration set or a scalar backed enumeration set which don't
need any methods.
Moreover, the implementation details of it leak quite a lot and I'm not
really a fan of having the reflection class of enum extend the class one.
My reasoning is that, albeit extremely clunky, one can already achieve the
kind of enumeration proposed here in userland, in a type safe fashion, in
PHP 8.0 by using union types:
final class Heart {}
final class Clubs {}
final class Spades {}
final class Diamond {}
final class Suit {
public function __construct(private Heart|Clubs|Spades|Diamond $value)
{}
public function value() { return $this->value; }
}
This form is basically reducing an ADT to an enumeration, and in any case
introducing additional syntax to alleviate the verbosity of this is a
separate concern.
(to see this somewhat in action I've written some slides for small
university talk which demonstrates how it can be used [1][2]).
The final concern, which might be non-existent as there is no mention of it
in the RFC, is OPcache.
My understanding of OPcache is fairly limited, but IIRC caching and
optimizing things around objects is extremely complicated if not at all
impossible, wouldn't this rather severe limitation also apply to enums,
even for basic lists?
Or are all the objectness restrictions placed on enums sufficient to make
this a non-issue?
As such, unless convinced otherwise, I don't think I will personally
support this proposal.
Best regards,
George P. Banyard
[1]
https://slides.com/girgias/reflection-algebraic-data-types-for-mortals#/5/5
[2]
https://slides.com/girgias/reflection-algebraic-data-types-for-mortals#/5/6
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to
offer you round 2 on enumerations. This is in the spirit of the
previous discussion, but based on that discussion a great deal has been
reworked. The main change is that Enumeration Cases are now object
instances of the Enumeration class rather than their own class. Most
of the other changes are knock-on effects of that.Of particular note:
- Cases may not have methods or constants on them. They're just dumb values.
- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given
how much was updated.https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in
reflection, and some opcache bugs that Ilija is still stomping on.There are a few outstanding questions listed that we would like
feedback on. We're not entirely certain which direction to go with
them, for reasons explained in the RFC. Input on those is especially
welcome.Happy New Year. May it be enumerable.
And we're back again. The RFC has been updated with a steady stream of smaller improvements based on feedback and testing, and is now in its Final Form(tm) (we think). The only major change worth noting is that we renamed things. :-)
An enum with no scalar backing is now called a Pure Enum, made up of Pure Cases. One that does have backing values is called a Backed Enum, made up of Backed Cases. That change is mainly to allow for future expansion to non-scalar backing static values, should the use case arise. Reflection was also reworked a bit to make it more logical.
https://wiki.php.net/rfc/enumerations
At this point, Ilija and I consider the RFC done and ready for a vote. Baring any major issues being brought up, we plan to start the vote in the first half of next week, probably Tuesday-ish. If you have any other bug reports or tweaks, please speak now or forever hold your patches.
--Larry Garfield
On Fri, Jan 29, 2021 at 6:18 PM Larry Garfield larry@garfieldtech.com
wrote:
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to
offer you round 2 on enumerations. This is in the spirit of the
previous discussion, but based on that discussion a great deal has been
reworked. The main change is that Enumeration Cases are now object
instances of the Enumeration class rather than their own class. Most
of the other changes are knock-on effects of that.Of particular note:
- Cases may not have methods or constants on them. They're just dumb
values.- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given
how much was updated.https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in
reflection, and some opcache bugs that Ilija is still stomping on.There are a few outstanding questions listed that we would like
feedback on. We're not entirely certain which direction to go with
them, for reasons explained in the RFC. Input on those is especially
welcome.Happy New Year. May it be enumerable.
And we're back again. The RFC has been updated with a steady stream of
smaller improvements based on feedback and testing, and is now in its Final
Form(tm) (we think). The only major change worth noting is that we renamed
things. :-)An enum with no scalar backing is now called a Pure Enum, made up of Pure
Cases. One that does have backing values is called a Backed Enum, made up
of Backed Cases. That change is mainly to allow for future expansion to
non-scalar backing static values, should the use case arise. Reflection
was also reworked a bit to make it more logical.https://wiki.php.net/rfc/enumerations
At this point, Ilija and I consider the RFC done and ready for a vote.
Baring any major issues being brought up, we plan to start the vote in the
first half of next week, probably Tuesday-ish. If you have any other bug
reports or tweaks, please speak now or forever hold your patches.--Larry Garfield
Two notes:
“enum” becomes a language keyword, with the usual potential for naming
conflicts with existing global constants.
... and class names, which is probably more relevant here. It's worth
noting that this RFC renders many existing enum libraries unusable, such as
myclabs/php-enum, which defines an "abstract class Enum".
Both Pure Enums and Backed Enums implement an internal interface named
IterableEnum
I really don't like this interface name. We have an "iterable" type in PHP,
and having an IterableEnum that is not ... iterable would be somewhat
confusing. IIRC you previously called this UnitEnum, which is also not
great in that "unitary enum" is not particularly well-established
terminology, but I think it's still better than IterableEnum.
Regards,
Nikita
On Fri, Jan 29, 2021 at 6:18 PM Larry Garfield larry@garfieldtech.com
wrote:Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to
offer you round 2 on enumerations. This is in the spirit of the
previous discussion, but based on that discussion a great deal has been
reworked. The main change is that Enumeration Cases are now object
instances of the Enumeration class rather than their own class. Most
of the other changes are knock-on effects of that.Of particular note:
- Cases may not have methods or constants on them. They're just dumb
values.- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given
how much was updated.https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in
reflection, and some opcache bugs that Ilija is still stomping on.There are a few outstanding questions listed that we would like
feedback on. We're not entirely certain which direction to go with
them, for reasons explained in the RFC. Input on those is especially
welcome.Happy New Year. May it be enumerable.
And we're back again. The RFC has been updated with a steady stream of
smaller improvements based on feedback and testing, and is now in its Final
Form(tm) (we think). The only major change worth noting is that we renamed
things. :-)An enum with no scalar backing is now called a Pure Enum, made up of Pure
Cases. One that does have backing values is called a Backed Enum, made up
of Backed Cases. That change is mainly to allow for future expansion to
non-scalar backing static values, should the use case arise. Reflection
was also reworked a bit to make it more logical.https://wiki.php.net/rfc/enumerations
At this point, Ilija and I consider the RFC done and ready for a vote.
Baring any major issues being brought up, we plan to start the vote in the
first half of next week, probably Tuesday-ish. If you have any other bug
reports or tweaks, please speak now or forever hold your patches.--Larry Garfield
Two notes:
“enum” becomes a language keyword, with the usual potential for naming
conflicts with existing global constants.... and class names, which is probably more relevant here. It's worth
noting that this RFC renders many existing enum libraries unusable, such as
myclabs/php-enum, which defines an "abstract class Enum".Both Pure Enums and Backed Enums implement an internal interface named
IterableEnumI really don't like this interface name. We have an "iterable" type in
PHP, and having an IterableEnum that is not ... iterable would be somewhat
confusing. IIRC you previously called this UnitEnum, which is also not
great in that "unitary enum" is not particularly well-established
terminology, but I think it's still better than IterableEnum.Regards,
Nikita
I'm also not really happy with the ReflectionEnumPureCase vs
ReflectionEnumBackedCase split in the reflection API. I think there should
be some form of common base here. That could be ReflectionEnumBackedCase
extends ReflectionEnumCase extends ReflectionClassConstant, or it could be
ReflectionEnumUnitCase extends ReflectionClassConstant, where
getBackingValue() can return null (previous design). What was the
motivation for switching to two "unrelated" classes?
One more thing I'm missing in the RFC and implementation both is an API for
declaring enums from PHP extensions. As far as I can tell, this is
currently not possible.
Nikita
Am 29.01.2021 um 18:15 schrieb Larry Garfield larry@garfieldtech.com:
And we're back again. The RFC has been updated with a steady stream of smaller improvements based on feedback and testing, and is now in its Final Form(tm) (we think). The only major change worth noting is that we renamed things. :-)
An enum with no scalar backing is now called a Pure Enum, made up of Pure Cases. One that does have backing values is called a Backed Enum, made up of Backed Cases. That change is mainly to allow for future expansion to non-scalar backing static values, should the use case arise. Reflection was also reworked a bit to make it more logical.
https://wiki.php.net/rfc/enumerations https://wiki.php.net/rfc/enumerations
At this point, Ilija and I consider the RFC done and ready for a vote. Baring any major issues being brought up, we plan to start the vote in the first half of next week, probably Tuesday-ish. If you have any other bug reports or tweaks, please speak now or forever hold your patches.
Hey,
There is a single point in the RFC which is unreasonable to me, which is barring static properties.
I do not consider static properties to be handled the same than normal properties. It's perfectly reasonable to prevent normal instance properties if we want [pure] enums to be stateless (which makes sense).
It however does not make sense to arbitrarily restrict static properties. In the end a static property (on final classes at least) is mostly a nice way to express global state, which could be written "global $my_namespaced_enum_class_prop_name;" and used then. It's just that self::$prop_name is much more readable and user-friendly, including for types and static analysis.
I also agree with Nikitas note that IterableEnum is really a suboptimal name. If you want to highlight finiteness, maybe call it FiniteEnum. UnitEnum would be fine too.
Thanks,
Bob
--Larry Garfield
--
To unsubscribe, visit: https://www.php.net/unsub.php <https://www.php.net/unsub.php
On Fri, Jan 29, 2021 at 7:17 PM Larry Garfield larry@garfieldtech.com
wrote:
Hello, Internalians!
After considerable discussion and effort, Ilija and I are ready to
offer you round 2 on enumerations. This is in the spirit of the
previous discussion, but based on that discussion a great deal has been
reworked. The main change is that Enumeration Cases are now object
instances of the Enumeration class rather than their own class. Most
of the other changes are knock-on effects of that.Of particular note:
- Cases may not have methods or constants on them. They're just dumb
values.- Enums themselves may have methods, static methods, or constants.
- Traits are supported, as long as they don't have properties.
- The value() method on scalar enums is now a property.
The full RFC is here, and I recommend reading it again in full given
how much was updated.https://wiki.php.net/rfc/enumerations
The implementation is 98% complete; there's still a few lagging bits in
reflection, and some opcache bugs that Ilija is still stomping on.There are a few outstanding questions listed that we would like
feedback on. We're not entirely certain which direction to go with
them, for reasons explained in the RFC. Input on those is especially
welcome.Happy New Year. May it be enumerable.
And we're back again. The RFC has been updated with a steady stream of
smaller improvements based on feedback and testing, and is now in its Final
Form(tm) (we think). The only major change worth noting is that we renamed
things. :-)An enum with no scalar backing is now called a Pure Enum, made up of Pure
Cases. One that does have backing values is called a Backed Enum, made up
of Backed Cases. That change is mainly to allow for future expansion to
non-scalar backing static values, should the use case arise. Reflection
was also reworked a bit to make it more logical.https://wiki.php.net/rfc/enumerations
At this point, Ilija and I consider the RFC done and ready for a vote.
Baring any major issues being brought up, we plan to start the vote in the
first half of next week, probably Tuesday-ish. If you have any other bug
reports or tweaks, please speak now or forever hold your patches.--Larry Garfield
Hi,
Maybe IterableEnum can actually be just Enum. As you are not allowed to
implement it and/or define it, wouldn't it work? That's how it's named
internally in Java, not that it would matter but sometimes people forget
it's just syntactic sugar there as well (
https://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html): public
abstract class Enum<E extends Enum<E>> implements Comparable<E>,
Serializable.
Also in the interface I think you can include the name property, similarly
with how you did in BackedEnum interface.
https://wiki.php.net/rfc/property_accessors will probably allow it to be
clearly defined at some point.
A bit it bothers me that backed enums are very easy to implement even
without the special case and just a simple Enum would be fine.
I mean even if you will have a backed enum, with would be simple and
probably the need will come at some point to have also a public function
getLegacyValue(): string and a public static function
fromLegacyValue(string $value): Enum.
But yes, using backed values is a common pattern so I'm guessing it's a
valuable important use case.
For storing in a database purpose, property name can be used directly, I
think.
It would nice to have in the rfc the recommended way to retrieve the Enum,
given that you know the name.
I'm guessing that would be Suit::$name;
Thanks,
Alex
https://wiki.php.net/rfc/enumerations
At this point, Ilija and I consider the RFC done and ready for a vote.
Baring any major issues being brought up, we plan to start the vote in the
first half of next week, probably Tuesday-ish. If you have any other bug
reports or tweaks, please speak now or forever hold your patches.--Larry Garfield
Hi,
Maybe IterableEnum can actually be just Enum. As you are not allowed to
implement it and/or define it, wouldn't it work? That's how it's named
internally in Java, not that it would matter but sometimes people forget
it's just syntactic sugar there as well (
https://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html): public
abstract class Enum<E extends Enum<E>> implements Comparable<E>,
Serializable.
We tried that. The enum
keyword precludes any class or interface being called Enum
, even internally.
Also in the interface I think you can include the name property, similarly
with how you did in BackedEnum interface.
https://wiki.php.net/rfc/property_accessors will probably allow it to be
clearly defined at some point.A bit it bothers me that backed enums are very easy to implement even
without the special case and just a simple Enum would be fine.
I mean even if you will have a backed enum, with would be simple and
probably the need will come at some point to have also a public function
getLegacyValue(): string and a public static function
fromLegacyValue(string $value): Enum.
But yes, using backed values is a common pattern so I'm guessing it's a
valuable important use case.
It's more about standardizing the API. An ORM can rely on a backed enum always exposing its "value to save to the DB" at ->value, rather than it sometimes being ->value(), sometimes ->legacyValue(), sometimes ->asInt(), etc.
For storing in a database purpose, property name can be used directly, I
think.
It would nice to have in the rfc the recommended way to retrieve the Enum,
given that you know the name.
I'm guessing that would be Suit::$name;
That doesn't work for referencing a constant; it gets read as a static property. That's a more general syntactic question for PHP, and one we're not going to solve here. :-) Really, ->name is more an implementation artifact. If you want to have a primitive to pass around, for whatever reason, that's what a backed enum is for.
--Larry Garfield
We tried that. The
enum
keyword precludes any class or interface being
calledEnum
, even internally.
Enumerable, Enumerated, Enumerator?
On Mon, Feb 1, 2021 at 7:30 PM Larry Garfield larry@garfieldtech.com
wrote:
https://wiki.php.net/rfc/enumerations
At this point, Ilija and I consider the RFC done and ready for a vote.
Baring any major issues being brought up, we plan to start the vote in
the
first half of next week, probably Tuesday-ish. If you have any other
bug
reports or tweaks, please speak now or forever hold your patches.--Larry Garfield
Hi,
Maybe IterableEnum can actually be just Enum. As you are not allowed to
implement it and/or define it, wouldn't it work? That's how it's named
internally in Java, not that it would matter but sometimes people forget
it's just syntactic sugar there as well (
https://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html): public
abstract class Enum<E extends Enum<E>> implements Comparable<E>,
Serializable.We tried that. The
enum
keyword precludes any class or interface being
calledEnum
, even internally.Also in the interface I think you can include the name property,
similarly
with how you did in BackedEnum interface.
https://wiki.php.net/rfc/property_accessors will probably allow it to be
clearly defined at some point.A bit it bothers me that backed enums are very easy to implement even
without the special case and just a simple Enum would be fine.
I mean even if you will have a backed enum, with would be simple and
probably the need will come at some point to have also a public function
getLegacyValue(): string and a public static function
fromLegacyValue(string $value): Enum.
But yes, using backed values is a common pattern so I'm guessing it's a
valuable important use case.It's more about standardizing the API. An ORM can rely on a backed enum
always exposing its "value to save to the DB" at ->value, rather than it
sometimes being ->value(), sometimes ->legacyValue(), sometimes ->asInt(),
etc.For storing in a database purpose, property name can be used directly, I
think.
It would nice to have in the rfc the recommended way to retrieve the
Enum,
given that you know the name.
I'm guessing that would be Suit::$name;That doesn't work for referencing a constant; it gets read as a static
property. That's a more general syntactic question for PHP, and one we're
not going to solve here. :-) Really, ->name is more an implementation
artifact. If you want to have a primitive to pass around, for whatever
reason, that's what a backed enum is for.--Larry Garfield
--
To unsubscribe, visit: https://www.php.net/unsub.php
On Mon, Feb 1, 2021 at 9:30 PM Larry Garfield larry@garfieldtech.com
wrote:
https://wiki.php.net/rfc/enumerations
At this point, Ilija and I consider the RFC done and ready for a vote.
Baring any major issues being brought up, we plan to start the vote in
the
first half of next week, probably Tuesday-ish. If you have any other
bug
reports or tweaks, please speak now or forever hold your patches.--Larry Garfield
Hi,
Maybe IterableEnum can actually be just Enum. As you are not allowed to
implement it and/or define it, wouldn't it work? That's how it's named
internally in Java, not that it would matter but sometimes people forget
it's just syntactic sugar there as well (
https://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html): public
abstract class Enum<E extends Enum<E>> implements Comparable<E>,
Serializable.We tried that. The
enum
keyword precludes any class or interface being
calledEnum
, even internally.
I see. I made an assumption based on how I thought the situation should be.
Maybe that should not be impossible but I'm a bit further from
understanding how that could be fixed.
For storing in a database purpose, property name can be used directly, I
think.
It would nice to have in the rfc the recommended way to retrieve the
Enum,
given that you know the name.
I'm guessing that would be Suit::$name;That doesn't work for referencing a constant; it gets read as a static
property. That's a more general syntactic question for PHP, and one we're
not going to solve here. :-) Really, ->name is more an implementation
artifact. If you want to have a primitive to pass around, for whatever
reason, that's what a backed enum is for.
Yes, you are right. It would work however with constant(Suit::class . '::'
. $name) so someone could very easily define a static method for that and
serialization for a database is back for not backed enums.
Maybe there would be a way to remove the name public property completely to
avoid missusages?
So a "pure enum" should only be used in code as property, parameter, return
value types, reading from the constant and compared with identity operator
(match included).
Whenever that enum needs to be stored as a value as user defined
int/string, a backed enum should be used that has a public read-only value
property and a public static from method.
Yes... sounds good.
Alex
And we're back again. The RFC has been updated with a steady stream of
smaller improvements based on feedback and testing, and is now in its
Final Form(tm) (we think). The only major change worth noting is that
we renamed things. :-)An enum with no scalar backing is now called a Pure Enum, made up of
Pure Cases. One that does have backing values is called a Backed Enum,
made up of Backed Cases. That change is mainly to allow for future
expansion to non-scalar backing static values, should the use case
arise. Reflection was also reworked a bit to make it more logical.https://wiki.php.net/rfc/enumerations
At this point, Ilija and I consider the RFC done and ready for a vote.
Baring any major issues being brought up, we plan to start the vote in
the first half of next week, probably Tuesday-ish. If you have any
other bug reports or tweaks, please speak now or forever hold your
patches.--Larry Garfield
Based on feedback from Nikita we've made three last changes to Naming Things(tm).
- IterableEnum is back to "UnitEnum", which is the superset of Pure enums and Backed enums.
- The reflection classes were renamed accordingly.
- We clarified that tryFrom() will behave "as you'd expect" if passed an incorrect type, given strong vs weak mode.
And a few other inconsequential word tightenings.
Baring any other revelations, voting starts tomorrow.
--Larry Garfield