rfc:dnf_types
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
rfc:dnf_types [2021/11/04 15:12] – created crell | rfc:dnf_types [2023/01/21 22:12] (current) – Typo in code example, brakets are not allowed for standalone intersection types girgias | ||
---|---|---|---|
Line 2: | Line 2: | ||
* Version: 0.9 | * Version: 0.9 | ||
* Date: 2021-11-04 | * Date: 2021-11-04 | ||
- | * Author: George Peter Banyard, girgias@php.net; | + | * Author: George Peter Banyard |
- | * Status: | + | * Status: |
* First Published at: http:// | * First Published at: http:// | ||
Line 35: | Line 35: | ||
< | < | ||
- | // Accepts an object that implements both A and B, OR an object that implements D. | + | // Accepts an object that implements both A and B, |
- | A&B|D | + | // OR an object that implements D. |
+ | (A&B)|D | ||
- | // Accepts an object that implements C, OR a child of X that also implements D, OR null. | + | // Accepts an object that implements C, |
- | C|X& | + | // OR a child of X that also implements D, |
+ | // OR null. | ||
+ | C|(X&D)|null | ||
- | // Accepts an object that implements all three of A, B, and D, OR an int, OR null. | + | // Accepts an object that implements all three of A, B, and D, |
- | A& | + | // OR an int, |
+ | // OR null. | ||
+ | (A& | ||
</ | </ | ||
Line 49: | Line 54: | ||
< | < | ||
A&(B|D) | A&(B|D) | ||
- | // Can be rewritten as A& | + | // Can be rewritten as (A&B)|(A&D) |
A|(B& | A|(B& | ||
- | // Can be rewritten as A|B& | + | // Can be rewritten as A|(B&D)|(B&W)|null |
+ | </ | ||
+ | |||
+ | The order of types within each AND/OR section is irrelevant, and thus the following type declarations are all equivalent: | ||
+ | |||
+ | < | ||
+ | (A& | ||
+ | (B& | ||
+ | null|(C& | ||
</ | </ | ||
Requiring DNF for all type declarations allows conceptually all potential combinations of intersection and union rules, but in a standard fashion that is easier for the engine and easier for humans and static analyzers to comprehend. | Requiring DNF for all type declarations allows conceptually all potential combinations of intersection and union rules, but in a standard fashion that is easier for the engine and easier for humans and static analyzers to comprehend. | ||
- | === Return variance ==== | + | The parentheses around each intersection segment is required. |
+ | ==== Return co-variance ==== | ||
+ | When extending a class, a method return type may narrow only. That is, it must be the same or more restrictive as its parent. | ||
+ | <code php> | ||
+ | interface ITest { | ||
+ | public function stuff(): (A& | ||
+ | } | ||
+ | |||
+ | // Acceptable. | ||
+ | class TestOne implements ITest { | ||
+ | public function stuff(): A&B {} | ||
+ | } | ||
+ | |||
+ | // Acceptable. D is is a subset of A&B|D | ||
+ | class TestTwo implements ITest { | ||
+ | public function stuff(): D {} | ||
+ | } | ||
+ | |||
+ | // Acceptable, since C is a subset of A&B, | ||
+ | // even though it is not identical. | ||
+ | class TestThree implements ITest { | ||
+ | public function stuff(): C|D {} | ||
+ | } | ||
+ | |||
+ | // Not acceptable. This would allow an object that | ||
+ | // implements A but not B, which is wider than the interface. | ||
+ | class TestFour implements ITest { | ||
+ | public function stuff(): A|D {} | ||
+ | } | ||
+ | |||
+ | interface ITestTwo { | ||
+ | public function things(): C|D {} | ||
+ | } | ||
+ | |||
+ | // Not acceptable. Although C extends A and B, it's possible | ||
+ | // for an object to implement A and B without implementing C. | ||
+ | // Thus this definition is wider, and not allowed. | ||
+ | class TestFive implements ITestTwo { | ||
+ | public function things(): (A&B)|D {} | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Parameter contra-variance ==== | ||
+ | |||
+ | When extending a class, a method parameter type may widen only. That is, it must be the same or less restrictive as its parent. | ||
+ | |||
+ | <code php> | ||
+ | interface ITest { | ||
+ | public function stuff((A& | ||
+ | } | ||
+ | |||
+ | // Acceptable. Everything that ITest accepts is still valid | ||
+ | // and then some. | ||
+ | class TestOne implements ITest { | ||
+ | public function stuff((A& | ||
+ | } | ||
+ | |||
+ | // Acceptable. This accepts objects that implement just | ||
+ | // A, which is a super-set of those that implement A&B. | ||
+ | class TestOne implements ITest { | ||
+ | public function stuff(A|D $arg): void {} | ||
+ | } | ||
+ | |||
+ | // Not acceptable. The interface says D is acceptable, | ||
+ | // but this class does not. | ||
+ | class TestOne implements ITest { | ||
+ | public function stuff((A& | ||
+ | } | ||
+ | |||
+ | interface ITestTwo { | ||
+ | public function things(C|D $arg): void; | ||
+ | } | ||
+ | |||
+ | // Acceptable. Anything that implements C implements A&B, | ||
+ | // but this rule also allows classes that implement A&B | ||
+ | // directly, and thus is wider. | ||
+ | class TestFive implements ITestTwo { | ||
+ | public function things((A& | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Property invariance ==== | ||
+ | |||
+ | Object properties are already invariant in inheritance. This RFC does not change that. The type of a redeclared child property must logically match its parent. | ||
+ | |||
+ | ==== Duplicate and redundant types ==== | ||
+ | |||
+ | To catch some simple bugs in type declarations, | ||
+ | |||
+ | * Each segment of a DNF type must be unique. | ||
+ | |||
+ | A type declaration of '' | ||
+ | |||
+ | Note that a type declaration of '' | ||
+ | |||
+ | * Segments that are strict subsets of others are disallowed. | ||
+ | |||
+ | For example, the type definition '' | ||
+ | |||
+ | This does not guarantee that the type is " | ||
+ | |||
+ | ==== Reflection ==== | ||
+ | |||
+ | This RFC does not introduce any new reflection classes. | ||
+ | |||
+ | '' | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
- | None. | + | The sub-values of a '' |
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
Line 70: | Line 188: | ||
8.2 | 8.2 | ||
- | ===== Open Issues | + | ===== Vote ===== |
- | Make sure there are no open issues when the vote starts! | + | As per the voting RFC a yes/ |
- | ===== Unaffected PHP Functionality ===== | + | Voting started on 2022-06-17 and will end on 2022-07-01. |
- | + | <doodle title=" | |
- | List existing areas/features of PHP that will not be changed by the RFC. | + | * Yes |
- | + | * No | |
- | This helps avoid any ambiguity, shows that you have thought deeply about the RFC's impact, and helps reduces mail list noise. | + | </doodle> |
===== Future Scope ===== | ===== Future Scope ===== | ||
Line 84: | Line 202: | ||
==== Non-DNF types ==== | ==== Non-DNF types ==== | ||
- | In theory, supporting conjunctive normal form type definitions (and ANDed list of ORs) may be possible. | + | In theory, supporting conjunctive normal form type definitions (and ANDed list of ORs) or types which are not in a normalised form may be possible, either by supporting them directly or doing a compile time rewrite of the type expression. |
+ | |||
+ | However, as DNF is able to represent all reasonable boolean expressions | ||
==== Type aliasing ==== | ==== Type aliasing ==== | ||
Line 96: | Line 216: | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
- | Patch is available here: https:// | + | Patch is available here: https:// |
===== Implementation ===== | ===== Implementation ===== |
rfc/dnf_types.1636038760.txt.gz · Last modified: 2021/11/04 15:12 by crell