# [RFC] Enumerations, Round 2

- Thread: https://externals.io/message/112626
- Original archive: https://news-web.php.net/php.internals/112626
- Started by Larry Garfield on 2020-12-28T20:21:53+00:00
- Messages: 51
- RFC: Enumerations (Implemented (PHP 8.1)) — https://externals.io/rfc/enumerations.md
- RFC: Allow static properties in enums (Declined) — https://externals.io/rfc/enum_allow_static_properties.md
- Page 1 of 3: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/112626.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #112626 — Larry Garfield — 2020-12-28T20:21:53+00:00

URL: https://externals.io/message/112626#112626

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

---

## #112628 — Benjamin Morel — 2020-12-29T00:40:23+00:00

URL: https://externals.io/message/112626#112628 · In reply to: #112626

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

---

## #112629 — Larry Garfield — 2020-12-29T02:37:18+00:00

URL: https://externals.io/message/112626#112629 · In reply to: #112628

> 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

---

## #112631 — Marc — 2020-12-29T08:48:57+00:00

URL: https://externals.io/message/112626#112631 · In reply to: #112626

> 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.

---

## #112636 — Larry Garfield — 2020-12-29T15:42:04+00:00

URL: https://externals.io/message/112626#112636 · In reply to: #112631

> 
> 
> > […]
> 
> I did and the RFC looks really awesome :+1:
> 
> I don't have time to test the implementation but I noticed one thing:
> 
> > […]
> (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

---

## #112655 — Markus Fischer — 2020-12-30T08:43:33+00:00

URL: https://externals.io/message/112626#112655 · In reply to: #112626

Hi,


> The full RFC is here, and I recommend reading it again in full given how much was updated.
> 
> https://wiki.php.net/rfc/enumerations

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

---

## #112656 — drealecs@gmail.com — 2020-12-30T10:12:52+00:00

URL: https://externals.io/message/112626#112656 · In reply to: #112626

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.
- 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

---

## #112657 — Rowan Tommins — 2020-12-30T11:00:12+00:00

URL: https://externals.io/message/112626#112657 · In reply to: #112655

>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]

---

## #112658 — Rowan Tommins — 2020-12-30T12:27:28+00:00

URL: https://externals.io/message/112626#112658 · In reply to: #112626

> 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]

---

## #112663 — Markus Fischer — 2020-12-30T15:39:03+00:00

URL: https://externals.io/message/112626#112663 · In reply to: #112657

> 
> > […]
> 
> The question has no answer, because the declaration of the enum itself would be invalid:
> 
> > […]
> 
> 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

---

## #112664 — Benjamin Morel — 2020-12-30T15:48:49+00:00

URL: https://externals.io/message/112626#112664 · In reply to: #112663

>
> 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

---

## #112665 — Larry Garfield — 2020-12-30T16:07:34+00:00

URL: https://externals.io/message/112626#112665 · In reply to: #112655

> Hi,
> 
> 
> > […]
> 
> 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:
> > […]
> …
> > […]
> 
> 
> What in this case?
> 
> > […]
> …
> 
> 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

---

## #112672 — Aleksander Machniak — 2020-12-30T18:30:48+00:00

URL: https://externals.io/message/112626#112672 · In reply to: #112626

> 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

---

## #112680 — Larry Garfield — 2020-12-30T19:45:50+00:00

URL: https://externals.io/message/112626#112680 · In reply to: #112656

> 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

---

## #112681 — Larry Garfield — 2020-12-30T19:47:58+00:00

URL: https://externals.io/message/112626#112681 · In reply to: #112658

> 
> > […]
> 
> 
> 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

---

## #112684 — Larry Garfield — 2020-12-30T20:21:42+00:00

URL: https://externals.io/message/112626#112684 · In reply to: #112672

> 
> > […]
> 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

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

---

## #112687 — Aleksander Machniak — 2020-12-30T21:24:05+00:00

URL: https://externals.io/message/112626#112687 · In reply to: #112684

> > […]
> 
> 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?

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

---

## #112693 — michal.brzuchalski@gmail.com — 2020-12-31T10:02:52+00:00

URL: https://externals.io/message/112626#112693 · In reply to: #112626

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:

1. 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!

2. 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

---

## #112697 — Rowan Tommins — 2020-12-31T12:46:27+00:00

URL: https://externals.io/message/112626#112697 · In reply to: #112681

> 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]

---

## #112698 — Rowan Tommins — 2020-12-31T12:53:07+00:00

URL: https://externals.io/message/112626#112698 · In reply to: #112687

> 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]

---

## #112700 — Larry Garfield — 2020-12-31T17:15:41+00:00

URL: https://externals.io/message/112626#112700 · In reply to: #112698

> 
> > […]
> 
> 
> 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

---

## #112701 — Larry Garfield — 2020-12-31T18:13:57+00:00

URL: https://externals.io/message/112626#112701 · In reply to: #112693

> Hi Larry,

> I really like the shape of the current RFC.
> 
> I'd like to ask a few things:
> 
> 1. 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!

We originally used a method in the first draft.  We changed it to a property for three reasons.

1) 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.
2) 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.
3) 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.

> 2. 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

---

## #112702 — Benjamin Eberlei — 2021-01-01T18:08:33+00:00

URL: https://externals.io/message/112626#112702 · In reply to: #112626

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

---

## #112705 — Larry Garfield — 2021-01-01T19:30:38+00:00

URL: https://externals.io/message/112626#112705 · In reply to: #112702

> On Mon, Dec 28, 2020 at 9:22 PM Larry Garfield <larry@garfieldtech.com>
> wrote:
> 
> > […]
> 
> 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

---

## #112723 — Marc — 2021-01-03T10:56:26+00:00

URL: https://externals.io/message/112626#112723 · In reply to: #112636

> 
> > […]
> 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.
>
> > […]
> 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

---

Next page: https://externals.io/message/112626.md?page=2
