rfc:pure-intersection-types

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:pure-intersection-types [2021/05/17 08:50] – Rephrasing girgiasrfc:pure-intersection-types [2021/07/05 12:17] (current) – Mark as implemented girgias
Line 4: Line 4:
   * Date: 2021-03-23   * Date: 2021-03-23
   * Author: George Peter Banyard, <girgias@php.net>   * Author: George Peter Banyard, <girgias@php.net>
-  * Status: Under discussion+  * Status: Implemented 8.1 [[https://github.com/php/php-src/commit/069a9fa5e4478c7044cb6432258cfe207d10a202|https://github.com/php/php-src/commit/069a9fa5e4478c7044cb6432258cfe207d10a202]]
   * Target Version: PHP 8.1   * Target Version: PHP 8.1
   * Implementation: [[https://github.com/php/php-src/pull/6799|https://github.com/php/php-src/pull/6799]]   * Implementation: [[https://github.com/php/php-src/pull/6799|https://github.com/php/php-src/pull/6799]]
Line 38: Line 38:
   * Types are available through Reflection.   * Types are available through Reflection.
   * The syntax is a lot less boilerplate-y than phpdoc.   * The syntax is a lot less boilerplate-y than phpdoc.
 +
 +===== Motivation =====
 +
 +It is possible to emulate intersection types by creating a new interface which inherits from multiple ones, one such case is the built in ''%%SeekableIterator%%'' which extends the ''%%Iterator%%'' interface by adding a ''%%seek()%%'' method on it. However, an iterator can also be countable, an if a function needs to type against such a requirement the only possible way is to currently create a new interface:
 +
 +<code php>
 +interface CountableIterator extends Iterator, Countable {}
 +</code>
 +This works, but what if we want an iterator that is countable //and// seekable? We need to create another interface:
 +
 +<code php>
 +interface SeekableCountableIterator extends CountableIterator, SeekableIterator {}
 +</code>
 +As such, each new requirement necessitates the creation of various new interfaces taking into account all possible combinations.
 +
 +Moreover, the class needs to implement the specific interface and cannot rely on just implementing the base interfaces, meaning the introduction of such interfaces need to be propagated to all relevant classes, something which can be error prone. See this non-example:
 +
 +<code php>
 +interface A {}
 +interface B {}
 +interface AB extends A, B {}
 +
 +class Test implements A, B {}
 +
 +function foo(AB $v) {
 +    var_dump($v);
 +}
 +
 +foo(new Test());
 +</code>
 +Intersection types solve these issues.
  
 ===== Proposal ===== ===== Proposal =====
Line 61: Line 92:
 ==== Supported types ==== ==== Supported types ====
  
-Only class types (interfaces and class names) are supported by intersection types, this is because it is impossible for a value to be 2 different standard types at the same time.+Only class types (interfaces and class names) are supported by intersection types
 + 
 +The rationale is that for nearly all standard types using them in an intersection type result in a type which can never be satisfied (e.g. ''%%int&string%%''). 
 + 
 +Usage of ''%%mixed%%'' in an intersection type is redundant as ''%%mixed&T%%'' corresponds to ''%%T%%''as such this is disallowed. 
 + 
 +Similarly using ''%%iterable%%'' in an intersection results in a redundant invalid type, this can be seen by expanding the type expression ''%%iterable&T = (array|Traversable)&T = (array&T) | (Traversable&T) =  Traversable&T%%'' 
 + 
 +Although an intersection with ''%%callable%%'' //can// make sense (e.g. string&callable), we think it is unwise and points to a bug. 
 + 
 +Similarly ''%%parent%%'', ''%%self%%'', and ''%%static%%'' are technically feasible and //could// be used as part of an intersection, but impose strange restrictions on a child class which the base class violates or the base class already satisfies the type requirements in which case it is redundant. Therefore those 3 types are also forbidden because they likely point to a design issue.
  
 === Duplicate and redundant types === === Duplicate and redundant types ===
Line 104: Line 145:
  
 </code> </code>
 +
 ==== Variance ==== ==== Variance ====
  
Line 112: Line 154:
   * Property types are invariant (child must be subtype and supertype).   * Property types are invariant (child must be subtype and supertype).
  
-The only change is in how intersection types interact with subtyping, with one additional rule:+The only change is in how intersection types interact with subtyping, with two additional rules:
  
-  * An intersection ''%%I_1&...&I_n%%'' is a subtype of ''%%J_1&...&J_m%%'' if for all ''%%J_k%%''''%%I_l%%'' is not supertype of ''%%J_k%%''.+  * ''%%A%%'' is a subtype of ''%%B_1&...&B_n%%'' if for all ''%%B_i%%'', ''%%A%%'' is a subtype of ''%%B_i%%'' 
 +  * ''%%A_1&...&A_n%%'' is a subtype of ''%%B%%'' if there exists an ''%%A_i%%'' such that ''%%A_i%%'' is a subtype of ''%%B%%''
  
 In the following, some examples of what is allowed and what isn't are given. In the following, some examples of what is allowed and what isn't are given.
Line 358: Line 401:
 As per the voting RFC a yes/no vote with a 2/3 majority is needed for this proposal to be accepted. As per the voting RFC a yes/no vote with a 2/3 majority is needed for this proposal to be accepted.
  
-===== Patches and Tests ===== +<doodle title="Add pure intersection types to PHP" auth="girgias" voteType="single" closed="true"> 
- +   * Yes 
-Links to any external patches and tests go here. +   * No 
- +</doodle>
-If there is no patch, make it clear who will create a patch, or whether a volunteer to help with implementation is needed. +
- +
-Make it clear if the patch is intended to be the final patch, or is just a prototype. +
- +
-For changes affecting the core language, you should also provide a patch for the language specification.+
  
 ===== Implementation ===== ===== Implementation =====
  
-After the project is implemented, this section should contain+Implemented in PHP 8.1:
  
-  * the version(s) it was merged into +  * commit: https://github.com/php/php-src/commit/069a9fa5e4478c7044cb6432258cfe207d10a202 
-  * a link to the git commit(s) +  * docs: TDB
-  * a link to the PHP manual entry for the feature +
-  * a link to the language specification section (if any)+
  
 ===== Acknowledgements ===== ===== Acknowledgements =====
  
 To Ilija Tovilo for resolving the parser conflict with by-ref parameters. To Ilija Tovilo for resolving the parser conflict with by-ref parameters.
 +
 +To Nikita Popov for reviewing and refactoring the variance code.
  
 ===== References ===== ===== References =====
rfc/pure-intersection-types.txt · Last modified: 2021/07/05 12:17 by girgias