rfc:constants_in_traits

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:constants_in_traits [2022/06/30 23:14] sjirfc:constants_in_traits [2022/08/04 19:17] (current) – Move the status to implemented sji
Line 1: Line 1:
 ====== PHP RFC: Constants in Traits ====== ====== PHP RFC: Constants in Traits ======
-  * Version: 0.1+  * Version: 0.2.1
   * Date: 2022-06-21   * Date: 2022-06-21
   * Author: Shinji Igarashi<sji@sj-i.dev>, Stephen Reay<stephen@koalephant.com>   * Author: Shinji Igarashi<sji@sj-i.dev>, Stephen Reay<stephen@koalephant.com>
-  * Status: Under Discussion+  * Status: Implemented
   * Implementation: https://github.com/php/php-src/pull/8888   * Implementation: https://github.com/php/php-src/pull/8888
   * First Published at: http://wiki.php.net/rfc/constants_in_traits   * First Published at: http://wiki.php.net/rfc/constants_in_traits
Line 40: Line 40:
  
 <code php> <code php>
-intertface FooInterface {+interface FooInterface {
     public const FLAG_1 = 1;     public const FLAG_1 = 1;
     public function doFoo(int $flags): void;     public function doFoo(int $flags): void;
Line 66: Line 66:
 trait Foo { trait Foo {
     public const FLAG_1 = 1;     public const FLAG_1 = 1;
-    public const FLAG_2 = 2;+    protected const FLAG_2 = 2; 
 +    private const FLAG_3 = 2;
  
     public function doFoo(int $flags): void {     public function doFoo(int $flags): void {
Line 74: Line 75:
         if ($flags & self::FLAG_2) {         if ($flags & self::FLAG_2) {
             echo 'Got flag 2';             echo 'Got flag 2';
 +        }
 +        if ($flags & self::FLAG_3) {
 +            echo 'Got flag 3';
         }         }
     }     }
Line 218: Line 222:
         CONSTANT as private;         CONSTANT as private;
     }     }
 +}
 +</code>
 +
 +==== Can be used in Enum ====
 +Enumerations can use traits having constants, which behave as if the constants were defined within the enumeration.
 +
 +<code php>
 +trait T {
 +    private const CONSTANT = 42;
 +}
 +
 +// OK
 +enum E: int {
 +    use T;
 +
 +    case CaseA = self::CONSTANT;
 } }
 </code> </code>
Line 272: Line 292:
 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://externals.io/message/35800|[11]]]. This is beyond the scope of this proposal and would require a separate RFC.+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 is to introduce new visibility "trait local" that are only accessible within a given trait [[https://externals.io/message/35800|[11]]]. This is beyond the scope of this proposal and would require a separate RFC.
  
 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 in the first place ==== ==== Why are those compatibility checks performed on properties in the first place ====
-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 "overrides" for trait properties, for example, we might have to decide which definitions would "win out" at each location. If we were to allow some "overrides" for trait properties, for example, we might have to decide which definitions would "win out" at each location.
Line 283: Line 303:
 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 inheritance. In the original trait paper, it is assumed that the state is provided on the composing class side and accessed from traits through accessors [[https://www.cs.cmu.edu/~aldrich/courses/819/Scha03aTraits.pdf|[12]]]. This pure approach guides too much boilerplate in creating and using traits. +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 inheritance. In the original trait paper, it is assumed that the state is provided on the composing class side and accessed from traits through accessors [[https://www.cs.cmu.edu/~aldrich/courses/819/Scha03aTraits.pdf|[12]]]. This pure approach guides too much boilerplate in creating and using traits. 
  
-Historically, there have been two typical approaches to state conflicts in the diamond problem: one is to merge the state of the common ancestor, and the other is to have an independent state for each common ancestor in separate "paths" and provide a way to select one. Since different use cases require one or the other, programming languages sometimes have features that allow programmers to use these two methods selectively, such as virtual inheritance in C++.+Historically, there have been two typical approaches to state conflicts in multiple inheritance like the diamond problem: one is to merge states having the same name, and the other is to have an independent state for each ancestor in separate "paths" and provide a way to select one. Since different use cases require one or the other, programming languages sometimes have features that allow programmers to use these two methods selectively, such as virtual inheritance in C++.
  
 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. 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.
Line 310: Line 330:
 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 classes to be able to decide which members to expose.
  
 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 327: Line 347:
  
 ==== Rust ==== ==== 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://doc.rust-lang.org/reference/items/associated-items.html#associated-constants|[17]]][[https://doc.rust-lang.org/reference/paths.html#qualified-paths|[18]]].+Rust has associated constants. Associated 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://doc.rust-lang.org/reference/items/associated-items.html#associated-constants|[17]]][[https://doc.rust-lang.org/reference/paths.html#qualified-paths|[18]]].
  
 ==== C++ ==== ==== C++ ====
-In C++, multiple inheritance is possible, and constants in PHP can be defined as static const within classes. Members of the same name usually have different entities for each class, but by specifying virtual at the time of inheritance, the entities of common classes in diamond inheritance can be merged into one. If multiple member definitions of the same name conflict, they must be disambiguated on reference [[https://timsong-cpp.github.io/cppwp/n4861/class.mi|[19]]][[https://timsong-cpp.github.io/cppwp/n4861/class.member.lookup|[20]]].+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, the entities of common classes in diamond inheritance can be merged into one. If multiple member definitions of the same name conflict, they must be disambiguated on reference [[https://timsong-cpp.github.io/cppwp/n4861/class.mi|[19]]][[https://timsong-cpp.github.io/cppwp/n4861/class.member.lookup|[20]]].
  
 ==== Scala ==== ==== Scala ====
Line 336: Line 356:
  
  
-===== Proposed Voting Choices ===== +===== Vote ===== 
-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 started on 5. July 2022 and end on 19. July 2022 
 + 
 +<doodle title="Allow constants in traits as proposed?" auth="sji" voteType="single" closed="true"> 
 +   * Yes 
 +   * No 
 +</doodle> 
 + 
 + 
 +===== 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://link.springer.com/chapter/10.1007/978-3-540-71836-9_4|[22]]] default to trait state as trait local, but allow the programmer to selectively use merge behavior as needed. On the contrary, since PHP defaults to merge behavior, there may be a future extension that allows trait local to be explicitly declared. This option has even been mentioned in the old discussions, but it has not caught the attention of many people and has been on hold for more than a decade [[https://externals.io/message/35800|[11]]]. Perhaps it is time to reconsider this also. 
 + 
 +==== 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://docs.hhvm.com/hack/traits-and-interfaces/trait-and-interface-requirements|[23]]]. 
 + 
 +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://github.com/facebook/hhvm/commit/6a2c5150edf4c6d3d1e015d665d85221b0975f45|[24]]][[https://github.com/facebook/hhvm/commit/6dac173dd5cc56c67303f7e6a46917f0aebad773|[25]]]. Actually, trait constants in Hack were introduced as a syntactic sugar for traits that implement interfaces with constants [[https://github.com/facebook/hhvm/commit/9dcc664cd19d71c849be8ce052b11175d275c331|[26]]].  
 + 
 +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 364: Line 404:
   * [[https://timsong-cpp.github.io/cppwp/n4861/class.member.lookup|[20]]] Working Draft, Standard for Programming Language C++ n4861 | Member name lookup   * [[https://timsong-cpp.github.io/cppwp/n4861/class.member.lookup|[20]]] Working Draft, Standard for Programming Language C++ n4861 | Member name lookup
   * [[https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html|[21]]] Scala Language Specification Version 2.13 | Classes and Objects   * [[https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html|[21]]] Scala Language Specification Version 2.13 | Classes and Objects
 +  * [[https://link.springer.com/chapter/10.1007/978-3-540-71836-9_4|[22]]] Stateful Traits
 +  * [[https://docs.hhvm.com/hack/traits-and-interfaces/trait-and-interface-requirements|[23]]] HHVM and Hack Documentation | Traits And Interfaces: Trait And Interface Requirements
 +  * [[https://github.com/facebook/hhvm/commit/6a2c5150edf4c6d3d1e015d665d85221b0975f45|[24]]] facebook/hhvm | Allow traits to implement interfaces
 +  * [[https://github.com/facebook/hhvm/commit/6dac173dd5cc56c67303f7e6a46917f0aebad773|[25]]] facebook/hhvm | Allow traits to implement interfaces Add runtime support for implementing interfaces from traits
 +  * [[https://github.com/facebook/hhvm/commit/9dcc664cd19d71c849be8ce052b11175d275c331|[26]]] facebook/hhvm | Allow constants in traits
   * https://externals.io/message/110741 The initial discussion about constants in traits   * https://externals.io/message/110741 The initial discussion about constants in traits
- +
rfc/constants_in_traits.1656630871.txt.gz · Last modified: 2022/06/30 23:14 by sji