rfc:constants_in_traits
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
rfc:constants_in_traits [2022/06/29 05:16] – fix wording sji | rfc:constants_in_traits [2022/07/02 01:17] – bump the version number sji | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Constants in Traits ====== | ====== PHP RFC: Constants in Traits ====== | ||
- | * Version: 0.1 | + | * Version: 0.2 |
* Date: 2022-06-21 | * Date: 2022-06-21 | ||
* Author: Shinji Igarashi< | * Author: Shinji Igarashi< | ||
Line 8: | Line 8: | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | Traits are used for horizontal code reuse across classes, and currently allow the definition of methods and properties, but not constants. This means that it is not possible to define invariants expected by a trait in the trait itself. So currently, workarounds are required in some cases, such as defining constants in its composing class or an interface implemented by the composing class. | + | Traits |
This RFC proposes to allow defining constants in traits in the same manner that is currently possible for properties, with the following use cases in mind. | This RFC proposes to allow defining constants in traits in the same manner that is currently possible for properties, with the following use cases in mind. | ||
Line 80: | Line 80: | ||
==== Prohibit direct access through a trait name ==== | ==== Prohibit direct access through a trait name ==== | ||
- | Trait constants cannot be accessed through the name of the trait in a form like TraitName:: | + | Trait constants cannot be accessed through the name of the trait in a form like TraitName:: |
<code php> | <code php> | ||
Line 222: | Line 222: | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
- | There are no backwards-incompatible changes in this RFC. | + | There are no backward-incompatible changes in this RFC. |
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
Line 229: | Line 229: | ||
===== Discussions ===== | ===== Discussions ===== | ||
- | ==== Why were constants left out of traits | + | ==== Why were constants left out of traits |
- | It's an old story and no one remembers why, but it was probably simply overlooked as there was barely any mention of constants in traits in the old ML discussions. | + | It's an old story and no one remembers why, but it was probably simply overlooked as there was barely any mention of constants in traits in the old ML discussions |
- | [[https:// | + | The response from the original author of the trait RFC[[https:// |
< | < | ||
Line 268: | Line 268: | ||
</ | </ | ||
- | Creating an alias for a constant in a trait does not rewrite the method implementation in the trait to refer to that alias. Traits with constant definitions selected by the user can continue to refer to the correct invariants expected by the trait. However, for other traits the selected definition will be wrong invariants of the same name. '' | + | Creating an alias for a constant in a trait does not rewrite the method implementation in the trait to refer to that alias. Traits with constant definitions selected by the user can continue to refer to the correct invariants expected by the trait. However, for other traits the selected definition will be wrong invariants of the same name. '' |
While the main use case of trait methods is to be invoked from outside of the trait, the main use case of a trait constant is to serve as a member that is referenced by methods in the trait. Therefore, the same way of conflict resolution as for methods is not very useful. | While the main use case of trait methods is to be invoked from outside of the trait, the main use case of a trait constant is to serve as a member that is referenced by methods in the trait. Therefore, the same way of conflict resolution as for methods is not very useful. | ||
- | Currently, PHP has held off on better conflict resolution for trait properties for the last decade, and simply marks multiple incompatible definitions as errors, as an obvious sign of a mistake. One idea to address this issue is to introduce new visibility "trait local" that are only accessible within a given trait ([[https:// | + | Currently, PHP has held off on better conflict resolution for trait properties for the last decade, and simply marks multiple incompatible definitions as errors, as an obvious sign of a mistake. One idea to address this limitation |
Constants in PHP can hold state through object constants and are more similar to properties than methods. Both constants and properties should have the same style of conflict resolution. Therefore, for now, this RFC only proposes that trait constants have the same restrictions as properties. | Constants in PHP can hold state through object constants and are more similar to properties than methods. Both constants and properties should have the same style of conflict resolution. Therefore, for now, this RFC only proposes that trait constants have the same restrictions as properties. | ||
- | ==== Why are those compatibility checks performed on properties | + | ==== Why are those compatibility checks performed on properties |
- | It's basically a way to deal with state conflicts in the diamond problem. | + | It's basically a way to deal with state conflicts in multiple inheritance. |
If we were to allow some " | If we were to allow some " | ||
Line 283: | Line 283: | ||
There can be more than one policy for handling state conflicts, and PHP has implemented one restricted approach for now and has not yet addressed another policy after that. | There can be more than one policy for handling state conflicts, and PHP has implemented one restricted approach for now and has not yet addressed another policy after that. | ||
- | It should be noted that in the original trait paper, traits have only behavior and no state, thereby avoiding the state conflict problem in diamond | + | It should be noted that in the original trait paper, traits have only behavior and no state, thereby avoiding the state conflict problem in multiple |
- | Historically, | + | Historically, |
- | Where having a state becomes tricky is when the diamond problem occurs. If there are no conflicts, it does not matter if a trait has state. And even if there is a conflict, if the programming language defaults to either merge or having an independent state, that default will work fine for half of the use cases. | + | Where having a state becomes tricky is when conflicts occur. If there are no conflicts, it does not matter if a trait has state. And even if there is a conflict, if the programming language defaults to either merge or having an independent state, that default will work fine for half of the use cases. |
- | PHP strikes a balance in this problem, allowing traits to define properties, | + | PHP strikes a balance in this problem, allowing traits to define properties, choosing to deal with conflicts by merging states, and also marking any conflicting definitions with different visibility or default values as an error, as a sign of an unintended name conflict [[https:// |
==== Why not introduce visibility changes like methods ==== | ==== Why not introduce visibility changes like methods ==== | ||
Line 306: | Line 306: | ||
</ | </ | ||
- | Simply because it is not currently available for the property, this RFC doesn' | + | Simply because it is not currently available for properties, this RFC doesn' |
A survey of the 1149 packages on packagist shows that at least 222 locations in 26 packages use this feature for trait methods. | A survey of the 1149 packages on packagist shows that at least 222 locations in 26 packages use this feature for trait methods. | ||
- | We refrain from judging whether this is large or small, but there are probably cases where it is more convenient for classes to be able to decide which members to expose. | + | We refrain from judging whether this is large or small, but there are probably cases where it is more convenient for composing |
We do not preclude the future introduction of this feature, but for the sake of simplicity, we do not include it in this RFC. | We do not preclude the future introduction of this feature, but for the sake of simplicity, we do not include it in this RFC. | ||
Line 317: | Line 317: | ||
===== Comparison to other languages ===== | ===== Comparison to other languages ===== | ||
- | Hack gives priority to the definition of the trait used first when multiple trait constants conflict. If there is a conflict with a parent class, the parent class definition takes precedence, and if there is a conflict with an interface definition, it results in a type error. [[https:// | + | ==== Hack ==== |
+ | Hack gives priority to the definition of the trait used first when multiple trait constants conflict. If there is a conflict with a parent class, the parent class definition takes precedence, and if there is a conflict with an interface definition, it results in a type error. [[https:// | ||
+ | |||
+ | ==== Java ==== | ||
+ | In Java, variables can be defined in interfaces, which are public, static, and final by default, i.e., equivalent to constants in PHP. If a Java class implements multiple interfaces at the same time and their variables conflict, this is not an error in itself, but the programmer must explicitly specify which definition to choose when referencing those variables. If the same interface definition appears more than once via the diamond problem, the member is inherited only once [[https:// | ||
+ | |||
+ | ==== Kotlin ==== | ||
+ | In Kotlin, a class can implement multiple interfaces at the same time, and properties can be defined in interfaces. However, interface properties do not have actual backing fields. That is, they are abstract by default and must have their actual state in the implementing class, or provide accessors, with collision resolution if necessary [[https:// | ||
+ | |||
+ | ==== Rust ==== | ||
+ | Rust has associated constants. Asociated constants are constants associated with a type. If multiple definitions of the same name conflict, they must be disambiguated on reference using qualified paths [[https:// | ||
+ | |||
+ | ==== C++ ==== | ||
+ | In C++, multiple inheritance is possible, and equivalents of constants in PHP can be defined as static consts within classes. Members of the same name usually have different entities for each class, but by specifying virtual at the time of inheritance, | ||
+ | |||
+ | ==== Scala ==== | ||
+ | Scala allows multiple traits to be used simultaneously on an object, linearized into a single inheritance hierarchy based on the order in which they are specified. Constants can be defined with the keyword '' | ||
===== Proposed Voting Choices ===== | ===== Proposed Voting Choices ===== | ||
A 2/3 majority is needed for this RFC to pass. Voting will start on 5. July 2022 and end on 19. July 2022 | A 2/3 majority is needed for this RFC to pass. Voting will start on 5. July 2022 and end on 19. July 2022 | ||
+ | |||
+ | |||
+ | ===== Future Scope ===== | ||
+ | |||
+ | ==== Trait Local ==== | ||
+ | Since constants and properties of traits are primarily intended to be referenced from within traits, it is more useful to be able to restrict the scope to trait local and prevent conflicts in the first place. This is also true for methods that presume references from inside the trait itself, such as recursions. | ||
+ | |||
+ | Stateful Traits [[https:// | ||
+ | |||
+ | ==== Requiring specific interfaces or classes on traits ==== | ||
+ | Hack can require that the composing class of a trait be a derived class of a specific class or an implementation of a specific interface [[https:// | ||
+ | |||
+ | At the same time, Hack also allows to implement interfaces from traits. That is, if a class C uses a trait T that implements an interface I, then C is a subtype of I [[https:// | ||
+ | |||
+ | These can be other ways to tie properties or constants to traits, and this RFC does not preclude future implementation of any of them. | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
Line 326: | Line 358: | ||
===== References ===== | ===== References ===== | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
* https:// | * https:// | ||
- | * https:// | + | |
- | * https:// | + | |
- | * https:// | + |
rfc/constants_in_traits.txt · Last modified: 2022/08/04 19:17 by sji