Hi Larry and Ilja
It's great to see you're looking into enums, thanks! I have a few
considerations from a userland point of view. I've been maintaining a userland
enum implementation for a while now [1] so I think I share a thing or two about
my experience.
- Scalar enums are spot on, exactly what I'd expect.
- Support with match is awesome, and I think makes it so that array key support
isn't necessary.
- Others already addressed that serialization and deserialization would be a
nice feature. A common use case is to store enums in a datastore of some kind,
and it would be nice not having to make dedicated factories for them.
- The `case` syntax feels quirky. I assume it's because PHP wouldn't allow
something like this:
```
enum Suit: string {
Hearts = 'H';
Diamonds = 'D';
Clubs = 'C';
Spades = 'S';
}
```
Finally, I've got one (rather large) concern about object enums, specifically
with methods implemented on a per-enum basis. I did the same [2] when I first
implemented my userland package. From my research back then, I believe only
Java [3] allowed this behaviour. If you've checked out that link, you've seen
that value specific methods have been removed in v2. That's with good reason:
they turned out to rather cumbersome to maintain and even a bit useless. Here's
why:
- You example shows one method, the `color` one, which is still kind of
manageable. If you allow enum methods though, you'll often end up with more
than one method: `label`, `color`, `index`, `name`, `id`, are a few that come
to mind. In the end an enum grows very large and unmanageable, with often lots
of repeated code.
- Enum value methods actually are the state pattern [4] in disguise. One
difference being that enums objects can't manage their own internal state, so
they become less useful in applying the state pattern compared to using classes.
I think enums shouldn't aim to solve the state pattern. It's out of scope for
what enums should do and their way of solving the state pattern will be worse
in practice compared to using classes. I'd say it would be good to keep the
defintion of enums in mind:
> "an enumerated type […] is a data type consisting of a set of named values
> called elements, members, enumeral, or enumerators of the type. The
> enumerator names are usually identifiers that behave as constants in the
> language." [5]
"Named values" and "constants" being the keywords here, there's no "behaviour"
implemented by enum values, which is why only a small amount of languages allow
this kind of functionality.
I realise enum objects might seem like a good idea to provide more
value-specific functionality in a concise way, but let's compare per-value
methods with a method on the base enum:
```
enum Suit implements Colorful {
case Hearts {
public function color(): string {
return "Red";
}
}
case Diamonds {
public function color(): string {
return "Red";
}
}
case Clubs {
public function color(): string {
return "Black";
}
}
case Spades {
public function color(): string {
return "Black";
}
}
public function shape(): string {
return "Rectangle";
}
}
```
vs
```
enum Suit implements Colorful {
case Hearts;
case Diamonds;
case Clubs;
case Spades;
public function color(): string {
return match ($this) {
Suit::Hearts, Suite::Diamonds => "Red",
Suit::Clubs, Suite::Spades => "Black",
}
}
}
```
In summary:
- If you'd use enum objects for "simple functionality", I'd say `match` will
always be the more concise way.
- If you'd use enum objects for handling complex state, you're better off using
classes and properly implementing the state pattern.
I don't think enum objects should be a blocker, if people _really_ want it then
fine. Based on my experience though, I'm rather sure that they won't be very
useful, and would love to hear your opinion on the matter.
Kind regards
Brent
[1] https://github.com/spatie/enum
[2] https://github.com/spatie/enum/tree/v1#enum-specific-methods
[3] https://www.geeksforgeeks.org/enum-in-java/
[4] https://en.wikipedia.org/wiki/State_pattern
[5] https://en.wikipedia.org/wiki/Enumerated_type
> On 7 Dec 2020, at 10:30, Rowan Tommins <[email protected]> wrote:
>
> On 07/12/2020 01:00, Paul Crovella wrote:
>> Instance state being global is a well-known problem with singletons.
>> Maybe don't use singletons then. Or simply document them as was done
>> in the RFC. I'd prefer the former since singletons don't seem to buy
>> much here but problems, though maybe I'm missing something.
>
>
> Yes, I think you are missing something - or maybe I am, because I honestly
> can't picture what it would look like for enums *not* to be singletons.
>
> Would Suit::Hearts be a constructor, producing a new instance each time, each
> with its own state? Would we then overload ===, so that Suit::Hearts ===
> Suit::Hearts was still true somehow?
>
>
> > In any case why is static state being (kinda sorta) restricted along with
> > it?
>
>
> On the face of it, I agree, static properties could be supported. But looking
> at the details of the current proposal, it might actually take some thought
> to make them feel natural. As I understand it, each case acts like a
> sub-class, which is useful for over-riding instance methods, but would mean a
> static property would be defined separately on each case:
>
> enum Suit {
> static $data;
> case Hearts;
> case Spades;
> case Clubs;
> case Diamonds;
> }
>
> Suit::$data = 42;
> $mySuit = Suit::Hearts;
> var_dump($mySuit::$data); // will not print 42, because Suit::Hearts::$data
> is a different property
>
>
> As Pierre says, the idea of backing enums onto objects is mostly an
> implementation detail; their fundamental design is based on how enums are
> generally used, and implemented in other languages.
>
> Rather than "objects which are a bit enum-like", it might be useful to frame
> them as "enums which are a bit object-like". The primary consistency needs to
> be with what people will expect an enum to do.
>
> Backing them onto objects makes it easy to add on any object-like behaviour
> that feels useful, but once we've added it, it's much harder to remove or
> change if we realise it's causing problems for users, or getting in the way
> of other features.
>
> That's why I was asking if you had use cases in mind, because I was starting
> from that position: assume they have *no* features, and add the ones we think
> are necessary and achievable.
>
>
> Regards,
>
> --
> Rowan Tommins (né Collins)
> [IMSoP]
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php