rfc:enumerations_and_adts
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revisionNext revisionBoth sides next revision | ||
rfc:enumerations_and_adts [2020/09/19 16:14] – created crell | rfc:enumerations_and_adts [2020/09/19 21:58] – crell | ||
---|---|---|---|
Line 9: | Line 9: | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | This RFC introduces Enumerations to PHP. Specifically, | + | This RFC introduces Enumerations to PHP. Specifically, |
Many languages have support for enumerations of some variety. | Many languages have support for enumerations of some variety. | ||
Line 15: | Line 15: | ||
The specific implementation here draws inspiration primarily from Swift, Rust, and Kotlin, but is not (nor is it intended as) a perfect 1:1 port of any of them. | The specific implementation here draws inspiration primarily from Swift, Rust, and Kotlin, but is not (nor is it intended as) a perfect 1:1 port of any of them. | ||
+ | The most popular case of enumerations is '' | ||
===== Proposal ===== | ===== Proposal ===== | ||
+ | ==== Basic enumerations ==== | ||
+ | This RFC introduces a new language construct, '' | ||
- | All the features and examples of the proposal. | + | <code php> |
+ | enum Suit { | ||
+ | case Hearts; | ||
+ | case Diamonds; | ||
+ | case Clubs; | ||
+ | case Spades; | ||
+ | } | ||
+ | </ | ||
- | To [[http:// | + | This declaration creates a new enumerated type named %%Suit%%, which has four and only four legal values: '' |
- | for inclusion | + | |
- | Remember | + | <code php> |
+ | $val = Suit:: | ||
+ | |||
+ | function pick_a_card(Suit $suit) { ... } | ||
+ | |||
+ | pick_a_card($val); | ||
+ | pick_a_card(Suit: | ||
+ | pick_a_card(' | ||
+ | </ | ||
+ | |||
+ | In the simple case, multiple cases may be defined on a single line. The following is semantically equivalent to the definition above. | ||
+ | |||
+ | <code php> | ||
+ | enum Suit { | ||
+ | case Hearts, Diamonds, Clubs, Spades; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | An Enumeration may have one or more '' | ||
+ | |||
+ | Cases are not backed by a primitive value. | ||
+ | |||
+ | <code php> | ||
+ | $a = Suit:: | ||
+ | $b = Suit:: | ||
+ | |||
+ | $a === $b; // true | ||
+ | |||
+ | |||
+ | $a instanceof Suit; // true | ||
+ | $a instanceof Suit:: | ||
+ | </ | ||
+ | |||
+ | [Note to Iliya: The last line there is the tricksy one we haven' | ||
+ | |||
+ | Each Case class includes a default '' | ||
+ | |||
+ | <code php> | ||
+ | print Suit:: | ||
+ | // prints " | ||
+ | </ | ||
+ | |||
+ | That function may be overridden if desired. | ||
+ | |||
+ | [To Ilija: Do we want this part or not? I only thought of it while writing this. I don't know if it's good or bad.] | ||
+ | |||
+ | Enumerated type Cases may be used in union type definitions. | ||
+ | |||
+ | <code php> | ||
+ | function gimmie_red_card(Suit:: | ||
+ | </ | ||
+ | |||
+ | ==== Enumerated Case Methods ==== | ||
+ | |||
+ | As both Enum Types and Enum Cases are implemented using classes, they may take methods. | ||
+ | |||
+ | <code php> | ||
+ | interface Colorful { | ||
+ | public function color(): string; | ||
+ | } | ||
+ | |||
+ | enum Suit implements Colorful { | ||
+ | case Hearts { | ||
+ | public function color(): string { | ||
+ | return " | ||
+ | } | ||
+ | }; // Note the semi-colon here! | ||
+ | |||
+ | case Diamonds { | ||
+ | public function color(): string { | ||
+ | return " | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | case Clubs { | ||
+ | public function color(): string { | ||
+ | return " | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | case Spades { | ||
+ | public function color(): string { | ||
+ | return " | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | public function shape(): string { | ||
+ | return " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function paint(Colorful $c) { ... } | ||
+ | |||
+ | paint(Suit:: | ||
+ | </ | ||
+ | |||
+ | In this example, all four Enum cases will have a method '' | ||
+ | |||
+ | Enum Cases may not implement interfaces themselves. | ||
+ | |||
+ | Static methods on Cases are not supported. | ||
+ | |||
+ | [Ilija: We haven' | ||
+ | |||
+ | Inside a method on a Case, The '' | ||
+ | |||
+ | (Note that in this case it would be a better data modeling practice to also define a '' | ||
+ | |||
+ | The above hierarchy is logically similar to the following class structure: | ||
+ | |||
+ | <code php> | ||
+ | interface Colorful { | ||
+ | public function color(): string; | ||
+ | } | ||
+ | |||
+ | abstract class Suit implements Colorful { | ||
+ | public function shape(): string { | ||
+ | return " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class Hearts extends Suit { | ||
+ | public function color(): string { | ||
+ | return " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class Diamonds extends Suit { | ||
+ | public function color(): string { | ||
+ | return " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class Clubs extends Suit { | ||
+ | public function color(): string { | ||
+ | return " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class Spades extends Suit { | ||
+ | public function color(): string { | ||
+ | return " | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Associated Values ==== | ||
+ | |||
+ | Enumerated Cases may optionally include associated values. | ||
+ | |||
+ | Associated values are defined using constructor property promotion. | ||
+ | |||
+ | <code php> | ||
+ | enum Distance { | ||
+ | case Kilometers(public int $num); | ||
+ | case Miles(public int $num); | ||
+ | } | ||
+ | |||
+ | $my_walk = Distance:: | ||
+ | // Named parameters work like any other function call. | ||
+ | $next_walk = Distance:: | ||
+ | |||
+ | print $my_walk-> | ||
+ | |||
+ | $my_walk === $next_walk; // FALSE! | ||
+ | </ | ||
+ | |||
+ | Enum Cases may not implement a full constructor. | ||
+ | |||
+ | An Enum Case that supports Associated Values is called an Associable Case. An Enum Case that does not have Associated Values is called a Unit Case. An Enumerated Type may consist of any combination of Associable and Unit Cases. | ||
+ | |||
+ | The Enum Type itself may not define associated values. | ||
+ | |||
+ | Associated values are always read-only, both internally to the class and externally. | ||
+ | |||
+ | Use cases that would require more complete class functionality (arbitrary properties, custom constructors, | ||
+ | |||
+ | ==== Match expressions ==== | ||
+ | |||
+ | When dealing with Unit Cases, '' | ||
+ | |||
+ | <code php> | ||
+ | $val = Suit:: | ||
+ | |||
+ | $str = match ($val) { | ||
+ | Suit:: | ||
+ | Suit::Clubs => " | ||
+ | Suit:: | ||
+ | default => "The shape of my heart", | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | That is not true when dealing with Associable Cases. | ||
+ | |||
+ | <code php> | ||
+ | $val = Distance:: | ||
+ | |||
+ | $str = match type ($val) { | ||
+ | Distance:: | ||
+ | Distance:: | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | [Ilija, your thoughts on this?] | ||
+ | |||
+ | ==== Examples ==== | ||
+ | |||
+ | Below are a few examples of Enums in action. | ||
+ | |||
+ | === Maybe === | ||
+ | |||
+ | The (in)famous Maybe Monad can be implemented like this: | ||
+ | |||
+ | <code php> | ||
+ | enum Maybe { | ||
+ | // This is a Unit Case. | ||
+ | case None { | ||
+ | public function bind(callable $f) { | ||
+ | return $this; | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | // This is an Associable Case. | ||
+ | case Some(private mixed $value) { | ||
+ | // Note that the return type can be the Enum itself, thus restricting the return | ||
+ | // value to one of the enumerated types. | ||
+ | public function bind(callable $f) { | ||
+ | // $f is supposed to return a Maybe itself. | ||
+ | return $f($this-> | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | // This method is available on both None and Some. | ||
+ | public function value(): mixed { | ||
+ | // Still need to sort out match() for this to make sense. | ||
+ | return match type ($this) { | ||
+ | Optional:: | ||
+ | Optional:: | ||
+ | }; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === State machine ==== | ||
+ | |||
+ | Enums make it straightforward to express finite state machines. | ||
+ | |||
+ | <code php> | ||
+ | enum OvenStatus { | ||
+ | |||
+ | case Off { | ||
+ | public function turnOn() { return OvenStatus:: | ||
+ | }; | ||
+ | |||
+ | case On { | ||
+ | public function turnOff() { return OvenStatus:: | ||
+ | public function idle() { return OvenStatus:: | ||
+ | }; | ||
+ | |||
+ | case Idle { | ||
+ | public function on() { return OvenStatus:: | ||
+ | }; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | In this example, the oven can be in one of three states (Off, On, and Idling, meaning the flame is not on but it will turn back on when it detects it needs to). However, it can never go from Off to Idle or Idle to Off; it must go through On state first. | ||
+ | |||
+ | (Additional methods are of course likely | ||
+ | |||
+ | === Single Associable Enums === | ||
+ | |||
+ | Because all properties on an Enum are readonly, they offer a back-door way to create immutable objects. | ||
+ | |||
+ | <code php> | ||
+ | enum Point { | ||
+ | case ThreeD(public $x, public $x, public $z); | ||
+ | } | ||
+ | |||
+ | $p = Point:: | ||
+ | |||
+ | print $p->y; // prints 5 | ||
+ | $p->z = 9; // throws an Error of some kind, TBD. | ||
+ | </ | ||
+ | |||
+ | This is not a specific design goal of the implementation, | ||
- | If applicable, you may wish to use the language specification as a reference. | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
Line 42: | Line 334: | ||
===== Open Issues ===== | ===== Open Issues ===== | ||
- | Make sure there are no open issues when the vote starts! | + | |
+ | We're still not sure what to do with '' | ||
+ | |||
+ | Details of how the object-ness of Enum Cases get exposed are still unclear. | ||
===== Unaffected PHP Functionality ===== | ===== Unaffected PHP Functionality ===== | ||
- | List existing areas/ | ||
- | This helps avoid any ambiguity, shows that you have thought deeply about the RFC's impact, and helps reduces mail list noise. | + | No existing functionality should be affected, other than the new " |
===== Future Scope ===== | ===== Future Scope ===== | ||
- | This section details areas where the feature might be improved | + | |
+ | ==== Case enumeration ==== | ||
+ | |||
+ | In some languages, it is possible to enumerate all possible values of an Enum Type. For now that functionality is not implemented, | ||
+ | |||
+ | ==== Pattern matching ==== | ||
+ | |||
+ | Most languages that have an equivalent of associated values also support pattern matching as a way to extract values from the Enum Case. Pattern matching allows for a single '' | ||
+ | |||
+ | For now, matching against the Enum Case and accessing properties directly (something not supported in most ADT-supporting languages) is "good enough" | ||
===== Proposed Voting Choices ===== | ===== Proposed Voting Choices ===== | ||
Line 73: | Line 376: | ||
===== References ===== | ===== References ===== | ||
- | Links to external references, discussions or RFCs | + | |
+ | [[https:// | ||
===== Rejected Features ===== | ===== Rejected Features ===== | ||
Keep this updated with features that were discussed on the mail lists. | Keep this updated with features that were discussed on the mail lists. |
rfc/enumerations_and_adts.txt · Last modified: 2020/12/04 23:26 by crell