intersection types and null for defaults, properties and return types

  115479
July 19, 2021 08:41 nicolas.grekas@gmail.com (Nicolas Grekas)
Hi all,

I want to bring your attention to a behavior that was mostly overlooked:

   1. it is not possible to use an intersection type with an argument that
   defaults to null
   2. it is not possible to use an intersection type with a nullable
   property (nor to make it default to null)
   3. it is not possible to use an intersection type with a nullable return
   type

Actually, 2. was possible until it was "closed" in
https://github.com/php/php-src/pull/7254

I reported these behavior and you might find some discussion about it in
https://bugs.php.net/81268

Looking at the past discussion on this list (
https://externals.io/message/113712) and at the rfc itself (
https://wiki.php.net/rfc/pure-intersection-types), this was mostly
overlooked.

That's why I'm posting this message. So that we can have that missing
discussion here.

To me, we are going to need (userland "we") these capabilities.

It's quite surprising to be "forced" to return something, or "forced" to
pass a value, when all other types in PHP allow "null". I know about the
null pattern, but it is quite uncommon in PHP, because "null" works just
great usually.

I feel like we "just" need to agree on a syntax to make this possible. It
was first suggested in the related PR to use "?A&B" (see
https://github.com/php/php-src/pull/6799#issuecomment-804761117)

This was rejected by the author with the reasoning that (?A)&B could mean
(?A)&B or ?(A&B) or even (?A)|B.

I personally don't think this ambiguity exists: (?A)|B is strictly the same
as A&B, thus ?A&B cannot also mean A&B, and I don't get how one could take
?A&B for  (?A)|B.

Another argument is that ?A&B might collide with a future syntax. But I
fail to see how. For sure we create examples of such collisions, but they
all look constructed to me.

Shouldn't we allow ?A&B ? Intersection types look unfinished to me without
compat with nullables.

Cheers,
Nicolas
  115482
July 19, 2021 09:21 azjezz@protonmail.com (azjezz)
-----------------------a41ed6cbdad3a27bbb42405f8c1e8d8e
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;charset=utf-8

Hello,

I personally don't consider a bug, but an expected behavior.

when writing:

```
T $x =3D null
```

it is the same as writing:

```
T|null $x =3D null
```

or

```
?T $x =3D null
```

however, when `T` is an intersection between `X` and `T` ( `X&Y` ), `X&Y $=
x =3D null` becomes `null|X&Y $x =3D null`, which is a combination between=
 union and intersection types, however, as the RFC stats, currently combin=
ation between union and intersection types is not support ( hence "pure" )=
..

for this to be allowed, PHP would need to support combining union and inte=
rsection types, preferable using parenthesis ( `X|(Y&Z) $x =3D null` or `(=
X&Y)|Z $x =3D null` ).

Regards,

Saif.

=E2=80=90=E2=80=90=E2=80=90=E2=80=90=E2=80=90=E2=80=90=E2=80=90 Original M=
essage =E2=80=90=E2=80=90=E2=80=90=E2=80=90=E2=80=90=E2=80=90=E2=80=90

On Monday, July 19th, 2021 at 9:41 AM, Nicolas Grekas grekas@gmai=
l.com> wrote:

> Hi all, > =
> I want to bring your attention to a behavior that was mostly overlooked: > =
> 1. it is not possible to use an intersection type with an argument that > =
> defaults to null > 2. it is not possible to use an intersection type with a nullable > =
> property (nor to make it default to null) > 3. it is not possible to use an intersection type with a nullable retur= n
> =
> type > =
> Actually, 2. was possible until it was "closed" in > =
> I reported these behavior and you might find some discussion about i= t in
> =
> Looking at the past discussion on this list ( > =
> https://externals.io/message/113712) and at the rfc itself ( > =
> overlooked. > =
> That's why I'm posting this message. So that we can have that missin= g
> =
> discussion here. > =
> To me, we are going to need (userland "we") these capabilities. > =
> It's quite surprising to be "forced" to return something, or "forced= " to
> =
> pass a value, when all other types in PHP allow "null". I know about= the
> =
> null pattern, but it is quite uncommon in PHP, because "null" works = just
> =
> great usually. > =
> I feel like we "just" need to agree on a syntax to make this possibl= e. It
> =
> was first suggested in the related PR to use "?A&B" (see > =
> This was rejected by the author with the reasoning that (?A)&B could= mean
> =
> (?A)&B or ?(A&B) or even (?A)|B. > =
> I personally don't think this ambiguity exists: (?A)|B is strictly t= he same
> =
> as A&B, thus ?A&B cannot also mean A&B, and I don't get how one coul= d take
> =
> ?A&B for (?A)|B. > =
> Another argument is that ?A&B might collide with a future syntax. Bu= t I
> =
> fail to see how. For sure we create examples of such collisions, but= they
> =
> all look constructed to me. > =
> Shouldn't we allow ?A&B ? Intersection types look unfinished to me w= ithout
> =
> compat with nullables. > =
> Cheers, > =
> Nicolas -----------------------a41ed6cbdad3a27bbb42405f8c1e8d8e--
  115490
July 19, 2021 14:04 larry@garfieldtech.com ("Larry Garfield")
On Mon, Jul 19, 2021, at 4:21 AM, azjezz wrote:
> Hello, > > I personally don't consider a bug, but an expected behavior. > > when writing: > > ``` > T $x = null > ``` > > it is the same as writing: > > ``` > T|null $x = null > ``` > > or > > ``` > ?T $x = null > ``` > > however, when `T` is an intersection between `X` and `T` ( `X&Y` ), > `X&Y $x = null` becomes `null|X&Y $x = null`, which is a combination > between union and intersection types, however, as the RFC stats, > currently combination between union and intersection types is not > support ( hence "pure" ). > > for this to be allowed, PHP would need to support combining union and > intersection types, preferable using parenthesis ( `X|(Y&Z) $x = null` > or `(X&Y)|Z $x = null` ). > > Regards, > > Saif.
I can see the argument that "I need something with these two interfaces... or nothing" is a valid use case to care about. However, as Saif noted, this is a side effect of the inability to mix union and intersection types. So, my question would be, how feasible will it be in the future to introduce that mixing? If it's something that is likely to show up in 8.2 or 8.3, we may be better off going "meh" for now and letting a solution fall out naturally later. If it's something that could take a decade, then figuring out a way to special case "or null" in the meantime is probably worthwhile. (I don't know what the best way to do that is.) --Larry Garfield
  115493
July 19, 2021 14:26 guilliam.xavier@gmail.com (Guilliam Xavier)
On Mon, Jul 19, 2021 at 4:05 PM Larry Garfield <larry@garfieldtech.com>
wrote:

> > I can see the argument that "I need something with these two interfaces... > or nothing" is a valid use case to care about. However, as Saif noted, > this is a side effect of the inability to mix union and intersection types. > > So, my question would be, how feasible will it be in the future to > introduce that mixing? If it's something that is likely to show up in 8.2 > or 8.3, we may be better off going "meh" for now and letting a solution > fall out naturally later. If it's something that could take a decade, then > figuring out a way to special case "or null" in the meantime is probably > worthwhile. (I don't know what the best way to do that is.) >
You made me remember that [we had non-scalar parameter type declarations since PHP 5, and] we got scalar type declarations and return type declarations in 7.0 but nullable type declarations "only" in 7.1 (but I agree). -- Guilliam Xavier
  115495
July 19, 2021 14:28 a.leathley@gmx.net (Andreas Leathley)
On 19.07.21 11:21, azjezz wrote:
> however, when `T` is an intersection between `X` and `T` ( `X&Y` ), `X&Y $x = null` becomes `null|X&Y $x = null`, which is a combination between union and intersection types, however, as the RFC stats, currently combination between union and intersection types is not support ( hence "pure" ). > > for this to be allowed, PHP would need to support combining union and intersection types, preferable using parenthesis ( `X|(Y&Z) $x = null` or `(X&Y)|Z $x = null` ). > Supporting a syntax like ?A&B could just be a special case just like ?A
was in the past, before actual union types existed. I can see how full support for union types and intersection types can get complicated (and may never make it into PHP, or need quite some time until it does), but pure intersection types that can be nullable seems like a relatively simple special case which would be very helpful in code, because you could have optional intersection types in arguments/properties, which is otherwise not possible (and one would have to fall back once again to annotations and less type safety). Nullable intersection types also do not touch upon the main problems of mixing union types and intersection types, namely in reflection, where it would just lead to "ReflectionType::allowsNull" to be true instead of false, but not change any other logic as far as I can tell.
  115491
July 19, 2021 14:09 guilliam.xavier@gmail.com (Guilliam Xavier)
On Mon, Jul 19, 2021 at 10:41 AM Nicolas Grekas grekas@gmail.com>
wrote:

> Hi all, > > I want to bring your attention to a behavior that was mostly overlooked: > > 1. it is not possible to use an intersection type with an argument that > defaults to null > 2. it is not possible to use an intersection type with a nullable > property (nor to make it default to null) > 3. it is not possible to use an intersection type with a nullable return > type > > Actually, 2. was possible until it was "closed" in > https://github.com/php/php-src/pull/7254 > > I reported these behavior and you might find some discussion about it in > https://bugs.php.net/81268 > > Looking at the past discussion on this list ( > https://externals.io/message/113712) and at the rfc itself ( > https://wiki.php.net/rfc/pure-intersection-types), this was mostly > overlooked. > > That's why I'm posting this message. So that we can have that missing > discussion here. > > To me, we are going to need (userland "we") these capabilities. > > It's quite surprising to be "forced" to return something, or "forced" to > pass a value, when all other types in PHP allow "null". I know about the > null pattern, but it is quite uncommon in PHP, because "null" works just > great usually. > > I feel like we "just" need to agree on a syntax to make this possible. It > was first suggested in the related PR to use "?A&B" (see > https://github.com/php/php-src/pull/6799#issuecomment-804761117) > > This was rejected by the author with the reasoning that (?A)&B could mean > (?A)&B or ?(A&B) or even (?A)|B. > > I personally don't think this ambiguity exists: (?A)|B is strictly the same > as A&B, thus ?A&B cannot also mean A&B, and I don't get how one could take > ?A&B for (?A)|B. >
To me, the `|` (instead of `&`) in the original "is it `(?A)|B` or `?(A&B)`" was just a blatant typo. By the way, I think you made a couple of typos yourself: - "Actually, 2. was possible until [GitHub PR 7254]": 2. wasn't possible (as shown in bugsnet #81268), I guess you meant 1. (i.e. https://github.com/php/php-src/pull/7254/commits/710332aec7adf0f729b927d6bb7ae33c38703ce7 ) - "(?A)|B is strictly the same as A&B": looks like you copy-pasted the original typo ;) Indeed, `(?A)&B`, i.e. `(null|A)&B` (currently both unsupported syntaxes), is necessarily the same as just `A&B`. As for supporting the `?A&B` syntax, several issues were raised: - Ambiguity: We would want the `?` "unary operator" to have a "lower precedence" than the `&` "binary operator" (like `null|A&B` or explicit `null|(A&B)`), which would be the opposite of familiar `!A&B` for "true" operators. There was also some opposition to "implicit precedence" / preference for requiring explicit grouping. - Inconsistency: The similar `?A|B` syntax was explicitly rejected when introducing union types. Moreover the future scope anticipates full composite types, allowing arbitrary `A&B|C` (or `(A&B)|C`). And the main obstacle to supporting general composite types (whatever the syntax): Unknown variance/LSP correctness (apparently complex to specify/implement): George expressed themself in https://wiki.php.net/rfc/pure-intersection-types#composite_types_ie_mixing_union_and_intersection_types :
> While early prototyping shows that supporting A&B|C without any grouping looks feasible, there are still many other considerations (e.g.
Reflection), but namely the variance rules and checks, which would be dramatically increased and prone to error. and in https://github.com/php/php-src/pull/6799#issuecomment-805452895 :
> the issue is less about parsing nor type checking (as my initial prototype shows although far from done), but the variance rules and checks,
it already needs a different nested loop order to traverse both the parent and the child type list just for the pure intersection type case, and at this time I haven't thought deeply about how to handle this nor have really any idea how to achieve this. (but someone else may be able to sort that out?) That said, to me it also feels like the `null` type/value is "special", and that PHP has a history of "nullability" being more than "just a union where one of the types is `null`", making "nullable intersection types" desirable (even without waiting for [hypothetical] full composite types). But I fear that most of the previous points still apply...
> Another argument is that ?A&B might collide with a future syntax. But I > fail to see how. For sure we create examples of such collisions, but they > all look constructed to me. > > Shouldn't we allow ?A&B ? Intersection types look unfinished to me without > compat with nullables. > > Cheers, > Nicolas >
-- Guilliam Xavier
  115494
July 19, 2021 14:26 nicolas.grekas@gmail.com (Nicolas Grekas)
> I want to bring your attention to a behavior that was mostly overlooked: >> >> 1. it is not possible to use an intersection type with an argument that >> defaults to null >> 2. it is not possible to use an intersection type with a nullable >> property (nor to make it default to null) >> 3. it is not possible to use an intersection type with a nullable >> return >> type >> >> Actually, 2. was possible until it was "closed" in >> https://github.com/php/php-src/pull/7254 >> >> I reported these behavior and you might find some discussion about it in >> https://bugs.php.net/81268 >> >> Looking at the past discussion on this list ( >> https://externals.io/message/113712) and at the rfc itself ( >> https://wiki.php.net/rfc/pure-intersection-types), this was mostly >> overlooked. >> >> That's why I'm posting this message. So that we can have that missing >> discussion here. >> >> To me, we are going to need (userland "we") these capabilities. >> >> It's quite surprising to be "forced" to return something, or "forced" to >> pass a value, when all other types in PHP allow "null". I know about the >> null pattern, but it is quite uncommon in PHP, because "null" works just >> great usually. >> >> I feel like we "just" need to agree on a syntax to make this possible. It >> was first suggested in the related PR to use "?A&B" (see >> https://github.com/php/php-src/pull/6799#issuecomment-804761117) >> >> This was rejected by the author with the reasoning that (?A)&B could mean >> (?A)&B or ?(A&B) or even (?A)|B. >> >> I personally don't think this ambiguity exists: (?A)|B is strictly the >> same >> as A&B, thus ?A&B cannot also mean A&B, and I don't get how one could take >> ?A&B for (?A)|B. >> > > To me, the `|` (instead of `&`) in the original "is it `(?A)|B` or > `?(A&B)`" was just a blatant typo. By the way, I think you made a couple of > typos yourself: [...] >
You're right, sorry about that... As for supporting the `?A&B` syntax, several issues were raised:
> - Ambiguity: We would want the `?` "unary operator" to have a "lower > precedence" than the `&` "binary operator" (like `null|A&B` or explicit > `null|(A&B)`), which would be the opposite of familiar `!A&B` for "true" > operators. There was also some opposition to "implicit precedence" / > preference for requiring explicit grouping. >
As I wrote in my original message, I don't think this one holds: ?X&Y cannot mean (?X)&Y because this is strictly and trivially equivalent to X&Y.
> - Inconsistency: The similar `?A|B` syntax was explicitly rejected when > introducing union types. Moreover the future scope anticipates full > composite types, allowing arbitrary `A&B|C` (or `(A&B)|C >
The features we ship should be fully fledged. I don't know the future, and nobody does. Anyway, this doesn't prevent accepting ?X&Y since the syntax in your example doesn't conflict.
> And the main obstacle to supporting general composite types (whatever the > syntax): Unknown variance/LSP correctness (apparently complex to > specify/implement): George expressed themself in > https://wiki.php.net/rfc/pure-intersection-types#composite_types_ie_mixing_union_and_intersection_types > : >
Here is the PR, no need for suppositions. Please send me test cases about those unknown and let's discover them if they need to. https://github.com/php/php-src/pull/7259
> That said, to me it also feels like the `null` type/value is "special", > and that PHP has a history of "nullability" being more than "just a union > where one of the types is `null`", making "nullable intersection types" > desirable (even without waiting for [hypothetical] full composite types). >
Absolutely, null is special in the codebase, that's why the patch in the linkedPR is trivial. Thanks for your answer btw ! Nicolas
  115498
July 19, 2021 15:09 Danack@basereality.com (Dan Ackroyd)
nicolas-grekas wrote on the PR:
> ?X&Y cannot be confused with
It confused me. A compiler might understand it, but as a human I have trouble understanding it. Trowski wrote:
> The syntax should be either ?(X&Y) or (X&Y)|null
Non-ambiguous syntax is much better than ambiguous syntax. On Mon, 19 Jul 2021 at 15:26, Nicolas Grekas grekas@gmail.com> wrote:
> I don't know the future, and nobody does.
Maybe not, but we can look at the past and make predictions. Allowing null when then parameter type does not include null is supported in function parameters for legacy reasons: ``` class Bar{} function foo(Bar $bar = null) {} // parameter has implicit nullability. ``` But it was deliberately excluded from typed-properties: ``` class Bar{} class Foo { private Bar $bar = null; } PHP Fatal error: Default value for property of type Bar may not be null. Use the nullable type ?Bar to allow null default value in .... ``` Instead, you are required to be explicit on the type declaration: class Foo { private ?Bar $bar = null; } Prediction number 1: At some point implicit type nullability of function parameters will be deprecated. People should consider changing any code they have from implicit declaration to explicit. e.g. change: function foo(Bar $bar = null) {} to function foo(?Bar $bar) {} Prediction number 2: Having a null type in the language will happen before PHP 9, which will enable people to have many bike-shedding discussions over ?Bar vs Bar|null. But this discussion is moot for 8.1. This limitation might make intersection types not be considered usable by some projects, but the feature freeze is today. cheers Dan Ack
  115499
July 19, 2021 15:14 weirdan@gmail.com (Bruce Weirdan)
On Mon, Jul 19, 2021 at 6:09 PM Dan Ackroyd <Danack@basereality.com> wrote:

> Prediction number 2: Having a null type in the language will happen > before PHP 9, which will enable people to have many bike-shedding > discussions over ?Bar vs Bar|null.
Hasn't that already happened in 8.0? It seems to work [1] [1] https://3v4l.org/EfmJq -- Best regards, Bruce Weirdan mailto:weirdan@gmail.com
  115500
July 19, 2021 15:18 Danack@basereality.com (Dan Ackroyd)
On Mon, 19 Jul 2021 at 16:15, Bruce Weirdan <weirdan@gmail.com> wrote:
> > On Mon, Jul 19, 2021 at 6:09 PM Dan Ackroyd <Danack@basereality.com> wrote: > > > Prediction number 2: Having a null type in the language will happen > > before PHP 9, which will enable people to have many bike-shedding > > discussions over ?Bar vs Bar|null. > > Hasn't that already happened in 8.0? It seems to work [1] > > [1] https://3v4l.org/EfmJq >
Thanks. Wasn't aware/forgot that - but it's still not usable by itself, so is a bit of a hack. https://3v4l.org/LL9UI function f(): null { return null; } Fatal error: Null can not be used as a standalone type in Dan Ack
  115501
July 19, 2021 16:25 guilliam.xavier@gmail.com (Guilliam Xavier)
On Mon, Jul 19, 2021 at 4:26 PM Nicolas Grekas grekas@gmail.com>
wrote:


Great! Thanks! Interesting how it works out-of-the-box with just this
addition in Zend/zend_language_parser.y:

```diff
 type_expr:
  type { $$ = $1; }
  | '?' type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; }
  | union_type { $$ = $1; }
  | intersection_type { $$ = $1; }
+ | '?' intersection_type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; }
 ;
```

On Mon, Jul 19, 2021 at 5:09 PM Dan Ackroyd <Danack@basereality.com> wrote:

> nicolas-grekas wrote on the PR: > > ?X&Y cannot be confused with > > It confused me. A compiler might understand it, but as a human I have > trouble understanding it. > > Trowski wrote: > > The syntax should be either ?(X&Y) or (X&Y)|null > > Non-ambiguous syntax is much better than ambiguous syntax. >
Maybe it's just a matter of habit? For instance I got used to seeing things like `!$x = f()` (e.g. `if (!$x = f()) { throw /*...*/; } /* use $x */`) because some CS consider explicit parentheses in `!($x = f())` redundant (as PHP has a special case that "overrides" the normal precedence `(!$x) = f()` which would be an error). If you first consider `X&Y` as a type "unit", then it makes sense to make it "nullable" by prefixing it with `?`, I think?
> > But this discussion is moot for 8.1. > > This limitation might make intersection types not be considered usable > by some projects, but the feature freeze is today. >
Which can also be reversed: "The feature freeze is today, but this limitation might make intersection types not be considered usable by some projects"? (playing devil's advocate, I don't master the process) Regards, -- Guilliam Xavier
  115502
July 19, 2021 18:16 george.banyard@gmail.com ("G. P. B.")
On Mon, 19 Jul 2021 at 18:26, Guilliam Xavier xavier@gmail.com>
wrote:

> On Mon, Jul 19, 2021 at 4:26 PM Nicolas Grekas grekas@gmail.com> > wrote: > > > > > https://github.com/php/php-src/pull/7259 > > > > Great! Thanks! Interesting how it works out-of-the-box with just this > addition in Zend/zend_language_parser.y: > > ```diff > type_expr: > type { $$ = $1; } > | '?' type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } > | union_type { $$ = $1; } > | intersection_type { $$ = $1; } > + | '?' intersection_type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } > ; > ``` > > On Mon, Jul 19, 2021 at 5:09 PM Dan Ackroyd <Danack@basereality.com> > wrote: > > > nicolas-grekas wrote on the PR: > > > ?X&Y cannot be confused with > > > > It confused me. A compiler might understand it, but as a human I have > > trouble understanding it. > > > > Trowski wrote: > > > The syntax should be either ?(X&Y) or (X&Y)|null > > > > Non-ambiguous syntax is much better than ambiguous syntax. > > > > Maybe it's just a matter of habit? > For instance I got used to seeing things like `!$x = f()` (e.g. `if (!$x = > f()) { throw /*...*/; } /* use $x */`) because some CS consider explicit > parentheses in `!($x = f())` redundant (as PHP has a special case that > "overrides" the normal precedence `(!$x) = f()` which would be an error). > If you first consider `X&Y` as a type "unit", then it makes sense to make > it "nullable" by prefixing it with `?`, I think? > > > > > > But this discussion is moot for 8.1. > > > > This limitation might make intersection types not be considered usable > > by some projects, but the feature freeze is today. > > > > Which can also be reversed: "The feature freeze is today, but this > limitation might make intersection types not be considered usable by some > projects"? (playing devil's advocate, I don't master the process) > > Regards, > > -- > Guilliam Xavier >
Since when is usability for a specific project a consideration an RFC needs to have? If Symfony can't use it in its current state tough luck, I'm sure plenty of other projects can, especially now that using 'new Class' is possible as a default object value. I frankly don't care about being able to have some sort of partial union possible with the usage of intersection types, because it seems the machinery in the engine which makes null work, should also allow any standard PHP types as those are part of a bitflag and handling variance with them seems to work just fine... But for the love of god, this proposed syntax is horrendous, saying ?X&Y is unambiguous is disingenuous. It can either mean (?X)&Y = (X|null)&Y or ?(X&Y) = (X&Y)|null, the former one being bogus as null&Y is an impossible type, something which should error out to point out a potential bug in the same way we do for redundant types that we know at compile time. And if we allow this syntax then we really should be allowing ?A|B which is dumb. (and no ?X&Y is NOT something I consider forward compatible when it is just going to become one more edge case we need to maintain because people have no patience). If ?(X&Y) is allowed then ?(A|B) should also be allowed, and that needs an RFC for sure due to the controversy it had in the union type RFC. The only remaining sensible choice is (X&Y)|null / null|(X&Y), but as I said above if the machinery for null is there it must mean the machinery for int/string/array/float/bool is also there, and frankly being able to do something like (Traversable&Countable)|array is also extremely valuable, maybe even more than nullability, but in any case this is going to be confusing for end-users why only null (or standard PHP types) can be combined in a union with intersection types. That's one reason why it's only pure intersection types, if I had more time (or for crying out loud somebody would have paid me) to work on this I would have loved to get full composite types working. And I find it frankly insulting that in the four month this RFC has been published for discussion, with multiple push backs for voting due to bugs and me wanting that people know what implementation is - for the most part - going to land in php-src, this specific point has not been raised. It just feels like you are pissing on 6+ months of *unpaid* work I did because it doesn't suit your needs, and you just realised that and so decided to throw a wrench into a carefully crafted RFC to "fix" it by using a Twitter mob to put pressure on us/me. Maybe this topic didn't come up because for nearly everyone else "Pure Intersection Types" means what it says on the can, moreso that in the RFC the following line:
> This means it would *not* be possible to mix intersection and union types together such as A&B|C, this is left as a future scope
makes it clear, and most voters also understood that '?' is not a type "flag" but is syntactic sugar for 'null|'. There are plenty of issues with the implementation, from the whack parser hack, the non support for static/parent/self, to the complexity of the variance code, but I made all of those clear because they made *me* uncomfortable with the implementation. So if you are going to force this crappy syntax, then I'd rather axe this feature completely (and nobody can use/play with it) then have something I do not accept, nor are forced to accept because of a vote, land in core making PHP even more of an inconsistent joke. Best regards, George P. Banyard
  115516
July 20, 2021 09:43 ondrej@mirtes.cz (=?UTF-8?Q?Ond=C5=99ej_Mirtes?=)
I agree completely with George P. Banyard, these changes don’t seem very
well though-out. Pure intersection types are fine as accepted by the RFC.
Both ?A&B and (A&B)|null are confusing and would create holes in the type
system yet to be filled (but users would definitely try to use them which
would only lead to confusion and disappointment).

Ondřej Mirtes
  115522
July 20, 2021 11:07 nikita.ppv@gmail.com (Nikita Popov)
On Mon, Jul 19, 2021 at 8:16 PM G. P. B. banyard@gmail.com> wrote:

> On Mon, 19 Jul 2021 at 18:26, Guilliam Xavier xavier@gmail.com> > wrote: > > > On Mon, Jul 19, 2021 at 4:26 PM Nicolas Grekas grekas@gmail.com > > > > wrote: > > > > > > > > https://github.com/php/php-src/pull/7259 > > > > > > > Great! Thanks! Interesting how it works out-of-the-box with just this > > addition in Zend/zend_language_parser.y: > > > > ```diff > > type_expr: > > type { $$ = $1; } > > | '?' type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } > > | union_type { $$ = $1; } > > | intersection_type { $$ = $1; } > > + | '?' intersection_type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } > > ; > > ``` > > > > On Mon, Jul 19, 2021 at 5:09 PM Dan Ackroyd <Danack@basereality.com> > > wrote: > > > > > nicolas-grekas wrote on the PR: > > > > ?X&Y cannot be confused with > > > > > > It confused me. A compiler might understand it, but as a human I have > > > trouble understanding it. > > > > > > Trowski wrote: > > > > The syntax should be either ?(X&Y) or (X&Y)|null > > > > > > Non-ambiguous syntax is much better than ambiguous syntax. > > > > > > > Maybe it's just a matter of habit? > > For instance I got used to seeing things like `!$x = f()` (e.g. `if (!$x > = > > f()) { throw /*...*/; } /* use $x */`) because some CS consider explicit > > parentheses in `!($x = f())` redundant (as PHP has a special case that > > "overrides" the normal precedence `(!$x) = f()` which would be an error). > > If you first consider `X&Y` as a type "unit", then it makes sense to make > > it "nullable" by prefixing it with `?`, I think? > > > > > > > > > > But this discussion is moot for 8.1. > > > > > > This limitation might make intersection types not be considered usable > > > by some projects, but the feature freeze is today. > > > > > > > Which can also be reversed: "The feature freeze is today, but this > > limitation might make intersection types not be considered usable by some > > projects"? (playing devil's advocate, I don't master the process) > > > > Regards, > > > > -- > > Guilliam Xavier > > > > Since when is usability for a specific project a consideration an RFC needs > to have? > If Symfony can't use it in its current state tough luck, > I'm sure plenty of other projects can, especially now that using 'new > Class' is possible as a default object value. > > I frankly don't care about being able to have some sort of partial union > possible with the usage of intersection types, > because it seems the machinery in the engine which makes null work, should > also allow any standard PHP types as those are part of a bitflag and > handling variance with them seems to work just fine... > > But for the love of god, this proposed syntax is horrendous, saying ?X&Y is > unambiguous is disingenuous. > It can either mean (?X)&Y = (X|null)&Y or ?(X&Y) = (X&Y)|null, the former > one being bogus as null&Y is an impossible type, something which should > error out to point out a potential bug in the same way we do for redundant > types that we know at compile time. > And if we allow this syntax then we really should be allowing ?A|B which is > dumb. > (and no ?X&Y is NOT something I consider forward compatible when it is just > going to become one more edge case we need to maintain because people have > no patience). > > If ?(X&Y) is allowed then ?(A|B) should also be allowed, and that needs an > RFC for sure due to the controversy it had in the union type RFC. > > The only remaining sensible choice is (X&Y)|null / null|(X&Y), but as I > said above if the machinery for null is there it must mean the machinery > for int/string/array/float/bool is also there, and frankly being able to do > something like (Traversable&Countable)|array is also extremely valuable, > maybe even more than nullability, but in any case this is going to be > confusing for end-users why only null (or standard PHP types) can be > combined in a union with intersection types. > > That's one reason why it's only pure intersection types, if I had more time > (or for crying out loud somebody would have paid me) to work on this I > would have loved to get full composite types working. > > And I find it frankly insulting that in the four month this RFC has been > published for discussion, with multiple push backs for voting due to bugs > and me wanting that people know what implementation is - for the most part > - going to land in php-src, this specific point has not been raised. > It just feels like you are pissing on 6+ months of *unpaid* work I did > because it doesn't suit your needs, and you just realised that and so > decided to throw a wrench into a carefully crafted RFC to "fix" it by using > a Twitter mob to put pressure on us/me. > > Maybe this topic didn't come up because for nearly everyone else "Pure > Intersection Types" means what it says on the can, moreso that in the RFC > the following line: > > This means it would *not* be possible to mix intersection and union types > together such as A&B|C, this is left as a future scope > makes it clear, and most voters also understood that '?' is not a type > "flag" but is syntactic sugar for 'null|'. > > There are plenty of issues with the implementation, from the whack parser > hack, the non support for static/parent/self, to the complexity of the > variance code, but I made all of those clear because they made *me* > uncomfortable with the implementation. > So if you are going to force this crappy syntax, then I'd rather axe this > feature completely (and nobody can use/play with it) then have something I > do not accept, nor are forced to accept because of a vote, land in core > making PHP even more of an inconsistent joke. >
Hey George, I don't think there's been any malicious intent here -- it was obvious to you and I that not allowing unions implies not allowing nullability, but I can see how people less familiar with our type system implementation would not make that connection. After all, we do provide the separate ?T syntax, even if it is an internal alias for T|null. It's an unfortunate fact of the RFC process that concerns are sometimes only raised when voting starts and people start looking at the implementation -- or in this case, when they test the implementation after it has landed... In any case, I think it's pretty clear by now that there are some fairly strong opinions both on what the syntax for this should be (between ?X&Y, ?(X&Y), (X&Y)|null and X&Y|null, all of which have reasonable arguments for and against) and whether we should have this special case at all. I'm not sure we will be able to reach a sufficient consensus in this discussion. For that reason, I think this change should go through the RFC process. We can grant this an exemption from the feature freeze (as a clarification of an accepted proposal), but it should still go through the process. Regards, Nikita
  115526
July 20, 2021 12:41 krakjoe@gmail.com (Joe Watkins)
Agree, an RFC looks like the only way.

This is not worth delaying a release for, nor is it worth postponing the
feature freeze date.

It seems reasonable to fix this after freeze, would prefer to reach
consensus before RC stage.

Cheers
Joe

On Tue, 20 Jul 2021 at 13:07, Nikita Popov ppv@gmail.com> wrote:

> On Mon, Jul 19, 2021 at 8:16 PM G. P. B. banyard@gmail.com> wrote: > > > On Mon, 19 Jul 2021 at 18:26, Guilliam Xavier xavier@gmail.com > > > > wrote: > > > > > On Mon, Jul 19, 2021 at 4:26 PM Nicolas Grekas < > nicolas.grekas@gmail.com > > > > > > wrote: > > > > > > > > > > > https://github.com/php/php-src/pull/7259 > > > > > > > > > > Great! Thanks! Interesting how it works out-of-the-box with just this > > > addition in Zend/zend_language_parser.y: > > > > > > ```diff > > > type_expr: > > > type { $$ = $1; } > > > | '?' type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } > > > | union_type { $$ = $1; } > > > | intersection_type { $$ = $1; } > > > + | '?' intersection_type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } > > > ; > > > ``` > > > > > > On Mon, Jul 19, 2021 at 5:09 PM Dan Ackroyd <Danack@basereality.com> > > > wrote: > > > > > > > nicolas-grekas wrote on the PR: > > > > > ?X&Y cannot be confused with > > > > > > > > It confused me. A compiler might understand it, but as a human I have > > > > trouble understanding it. > > > > > > > > Trowski wrote: > > > > > The syntax should be either ?(X&Y) or (X&Y)|null > > > > > > > > Non-ambiguous syntax is much better than ambiguous syntax. > > > > > > > > > > Maybe it's just a matter of habit? > > > For instance I got used to seeing things like `!$x = f()` (e.g. `if > (!$x > > = > > > f()) { throw /*...*/; } /* use $x */`) because some CS consider > explicit > > > parentheses in `!($x = f())` redundant (as PHP has a special case that > > > "overrides" the normal precedence `(!$x) = f()` which would be an > error). > > > If you first consider `X&Y` as a type "unit", then it makes sense to > make > > > it "nullable" by prefixing it with `?`, I think? > > > > > > > > > > > > > > But this discussion is moot for 8.1. > > > > > > > > This limitation might make intersection types not be considered > usable > > > > by some projects, but the feature freeze is today. > > > > > > > > > > Which can also be reversed: "The feature freeze is today, but this > > > limitation might make intersection types not be considered usable by > some > > > projects"? (playing devil's advocate, I don't master the process) > > > > > > Regards, > > > > > > -- > > > Guilliam Xavier > > > > > > > Since when is usability for a specific project a consideration an RFC > needs > > to have? > > If Symfony can't use it in its current state tough luck, > > I'm sure plenty of other projects can, especially now that using 'new > > Class' is possible as a default object value. > > > > I frankly don't care about being able to have some sort of partial union > > possible with the usage of intersection types, > > because it seems the machinery in the engine which makes null work, > should > > also allow any standard PHP types as those are part of a bitflag and > > handling variance with them seems to work just fine... > > > > But for the love of god, this proposed syntax is horrendous, saying ?X&Y > is > > unambiguous is disingenuous. > > It can either mean (?X)&Y = (X|null)&Y or ?(X&Y) = (X&Y)|null, the former > > one being bogus as null&Y is an impossible type, something which should > > error out to point out a potential bug in the same way we do for > redundant > > types that we know at compile time. > > And if we allow this syntax then we really should be allowing ?A|B which > is > > dumb. > > (and no ?X&Y is NOT something I consider forward compatible when it is > just > > going to become one more edge case we need to maintain because people > have > > no patience). > > > > If ?(X&Y) is allowed then ?(A|B) should also be allowed, and that needs > an > > RFC for sure due to the controversy it had in the union type RFC. > > > > The only remaining sensible choice is (X&Y)|null / null|(X&Y), but as I > > said above if the machinery for null is there it must mean the machinery > > for int/string/array/float/bool is also there, and frankly being able to > do > > something like (Traversable&Countable)|array is also extremely valuable, > > maybe even more than nullability, but in any case this is going to be > > confusing for end-users why only null (or standard PHP types) can be > > combined in a union with intersection types. > > > > That's one reason why it's only pure intersection types, if I had more > time > > (or for crying out loud somebody would have paid me) to work on this I > > would have loved to get full composite types working. > > > > And I find it frankly insulting that in the four month this RFC has been > > published for discussion, with multiple push backs for voting due to bugs > > and me wanting that people know what implementation is - for the most > part > > - going to land in php-src, this specific point has not been raised. > > It just feels like you are pissing on 6+ months of *unpaid* work I did > > because it doesn't suit your needs, and you just realised that and so > > decided to throw a wrench into a carefully crafted RFC to "fix" it by > using > > a Twitter mob to put pressure on us/me. > > > > Maybe this topic didn't come up because for nearly everyone else "Pure > > Intersection Types" means what it says on the can, moreso that in the RFC > > the following line: > > > This means it would *not* be possible to mix intersection and union > types > > together such as A&B|C, this is left as a future scope > > makes it clear, and most voters also understood that '?' is not a type > > "flag" but is syntactic sugar for 'null|'. > > > > There are plenty of issues with the implementation, from the whack parser > > hack, the non support for static/parent/self, to the complexity of the > > variance code, but I made all of those clear because they made *me* > > uncomfortable with the implementation. > > So if you are going to force this crappy syntax, then I'd rather axe this > > feature completely (and nobody can use/play with it) then have something > I > > do not accept, nor are forced to accept because of a vote, land in core > > making PHP even more of an inconsistent joke. > > > > Hey George, > > I don't think there's been any malicious intent here -- it was obvious to > you and I that not allowing unions implies not allowing nullability, but I > can see how people less familiar with our type system implementation would > not make that connection. After all, we do provide the separate ?T syntax, > even if it is an internal alias for T|null. > > It's an unfortunate fact of the RFC process that concerns are sometimes > only raised when voting starts and people start looking at the > implementation -- or in this case, when they test the implementation after > it has landed... > > In any case, I think it's pretty clear by now that there are some fairly > strong opinions both on what the syntax for this should be (between ?X&Y, > ?(X&Y), (X&Y)|null and X&Y|null, all of which have reasonable arguments for > and against) and whether we should have this special case at all. I'm not > sure we will be able to reach a sufficient consensus in this discussion. > > For that reason, I think this change should go through the RFC process. We > can grant this an exemption from the feature freeze (as a clarification of > an accepted proposal), but it should still go through the process. > > Regards, > Nikita >
  115533
July 20, 2021 15:06 george.banyard@gmail.com ("G. P. B.")
On Tue, 20 Jul 2021 at 14:41, Joe Watkins <krakjoe@gmail.com> wrote:

> Agree, an RFC looks like the only way. > > This is not worth delaying a release for, nor is it worth postponing the > feature freeze date. > > It seems reasonable to fix this after freeze, would prefer to reach > consensus before RC stage. > > Cheers > Joe > > On Tue, 20 Jul 2021 at 13:07, Nikita Popov ppv@gmail.com> wrote: > >> On Mon, Jul 19, 2021 at 8:16 PM G. P. B. banyard@gmail.com> >> wrote: >> >> > On Mon, 19 Jul 2021 at 18:26, Guilliam Xavier < >> guilliam.xavier@gmail.com> >> > wrote: >> > >> > > On Mon, Jul 19, 2021 at 4:26 PM Nicolas Grekas < >> nicolas.grekas@gmail.com >> > > >> > > wrote: >> > > >> > > > >> > > > https://github.com/php/php-src/pull/7259 >> > > > >> > > >> > > Great! Thanks! Interesting how it works out-of-the-box with just this >> > > addition in Zend/zend_language_parser.y: >> > > >> > > ```diff >> > > type_expr: >> > > type { $$ = $1; } >> > > | '?' type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } >> > > | union_type { $$ = $1; } >> > > | intersection_type { $$ = $1; } >> > > + | '?' intersection_type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } >> > > ; >> > > ``` >> > > >> > > On Mon, Jul 19, 2021 at 5:09 PM Dan Ackroyd <Danack@basereality.com> >> > > wrote: >> > > >> > > > nicolas-grekas wrote on the PR: >> > > > > ?X&Y cannot be confused with >> > > > >> > > > It confused me. A compiler might understand it, but as a human I >> have >> > > > trouble understanding it. >> > > > >> > > > Trowski wrote: >> > > > > The syntax should be either ?(X&Y) or (X&Y)|null >> > > > >> > > > Non-ambiguous syntax is much better than ambiguous syntax. >> > > > >> > > >> > > Maybe it's just a matter of habit? >> > > For instance I got used to seeing things like `!$x = f()` (e.g. `if >> (!$x >> > = >> > > f()) { throw /*...*/; } /* use $x */`) because some CS consider >> explicit >> > > parentheses in `!($x = f())` redundant (as PHP has a special case that >> > > "overrides" the normal precedence `(!$x) = f()` which would be an >> error). >> > > If you first consider `X&Y` as a type "unit", then it makes sense to >> make >> > > it "nullable" by prefixing it with `?`, I think? >> > > >> > > >> > > > >> > > > But this discussion is moot for 8.1. >> > > > >> > > > This limitation might make intersection types not be considered >> usable >> > > > by some projects, but the feature freeze is today. >> > > > >> > > >> > > Which can also be reversed: "The feature freeze is today, but this >> > > limitation might make intersection types not be considered usable by >> some >> > > projects"? (playing devil's advocate, I don't master the process) >> > > >> > > Regards, >> > > >> > > -- >> > > Guilliam Xavier >> > > >> > >> > Since when is usability for a specific project a consideration an RFC >> needs >> > to have? >> > If Symfony can't use it in its current state tough luck, >> > I'm sure plenty of other projects can, especially now that using 'new >> > Class' is possible as a default object value. >> > >> > I frankly don't care about being able to have some sort of partial union >> > possible with the usage of intersection types, >> > because it seems the machinery in the engine which makes null work, >> should >> > also allow any standard PHP types as those are part of a bitflag and >> > handling variance with them seems to work just fine... >> > >> > But for the love of god, this proposed syntax is horrendous, saying >> ?X&Y is >> > unambiguous is disingenuous. >> > It can either mean (?X)&Y = (X|null)&Y or ?(X&Y) = (X&Y)|null, the >> former >> > one being bogus as null&Y is an impossible type, something which should >> > error out to point out a potential bug in the same way we do for >> redundant >> > types that we know at compile time. >> > And if we allow this syntax then we really should be allowing ?A|B >> which is >> > dumb. >> > (and no ?X&Y is NOT something I consider forward compatible when it is >> just >> > going to become one more edge case we need to maintain because people >> have >> > no patience). >> > >> > If ?(X&Y) is allowed then ?(A|B) should also be allowed, and that needs >> an >> > RFC for sure due to the controversy it had in the union type RFC. >> > >> > The only remaining sensible choice is (X&Y)|null / null|(X&Y), but as I >> > said above if the machinery for null is there it must mean the machinery >> > for int/string/array/float/bool is also there, and frankly being able >> to do >> > something like (Traversable&Countable)|array is also extremely valuable, >> > maybe even more than nullability, but in any case this is going to be >> > confusing for end-users why only null (or standard PHP types) can be >> > combined in a union with intersection types. >> > >> > That's one reason why it's only pure intersection types, if I had more >> time >> > (or for crying out loud somebody would have paid me) to work on this I >> > would have loved to get full composite types working. >> > >> > And I find it frankly insulting that in the four month this RFC has been >> > published for discussion, with multiple push backs for voting due to >> bugs >> > and me wanting that people know what implementation is - for the most >> part >> > - going to land in php-src, this specific point has not been raised. >> > It just feels like you are pissing on 6+ months of *unpaid* work I did >> > because it doesn't suit your needs, and you just realised that and so >> > decided to throw a wrench into a carefully crafted RFC to "fix" it by >> using >> > a Twitter mob to put pressure on us/me. >> > >> > Maybe this topic didn't come up because for nearly everyone else "Pure >> > Intersection Types" means what it says on the can, moreso that in the >> RFC >> > the following line: >> > > This means it would *not* be possible to mix intersection and union >> types >> > together such as A&B|C, this is left as a future scope >> > makes it clear, and most voters also understood that '?' is not a type >> > "flag" but is syntactic sugar for 'null|'. >> > >> > There are plenty of issues with the implementation, from the whack >> parser >> > hack, the non support for static/parent/self, to the complexity of the >> > variance code, but I made all of those clear because they made *me* >> > uncomfortable with the implementation. >> > So if you are going to force this crappy syntax, then I'd rather axe >> this >> > feature completely (and nobody can use/play with it) then have >> something I >> > do not accept, nor are forced to accept because of a vote, land in core >> > making PHP even more of an inconsistent joke. >> > >> >> Hey George, >> >> I don't think there's been any malicious intent here -- it was obvious to >> you and I that not allowing unions implies not allowing nullability, but I >> can see how people less familiar with our type system implementation would >> not make that connection. After all, we do provide the separate ?T syntax, >> even if it is an internal alias for T|null. >> >> It's an unfortunate fact of the RFC process that concerns are sometimes >> only raised when voting starts and people start looking at the >> implementation -- or in this case, when they test the implementation after >> it has landed... >> >> In any case, I think it's pretty clear by now that there are some fairly >> strong opinions both on what the syntax for this should be (between ?X&Y, >> ?(X&Y), (X&Y)|null and X&Y|null, all of which have reasonable arguments >> for >> and against) and whether we should have this special case at all. I'm not >> sure we will be able to reach a sufficient consensus in this discussion. >> >> For that reason, I think this change should go through the RFC process. We >> can grant this an exemption from the feature freeze (as a clarification of >> an accepted proposal), but it should still go through the process. >> >> Regards, >> Nikita >> > If RMs and everyone else is okay for this being handled post feature freeze
via an RFC, I am fine with it too. Best regards George P. Banyard
  115539
July 21, 2021 07:05 nicolas.grekas@gmail.com (Nicolas Grekas)
Le mar. 20 juil. 2021 à 14:41, Joe Watkins <krakjoe@gmail.com> a écrit :

> Agree, an RFC looks like the only way. > > This is not worth delaying a release for, nor is it worth postponing the > feature freeze date. > > It seems reasonable to fix this after freeze, would prefer to reach > consensus before RC stage. > > Cheers > Joe > > On Tue, 20 Jul 2021 at 13:07, Nikita Popov ppv@gmail.com> wrote: > >> On Mon, Jul 19, 2021 at 8:16 PM G. P. B. banyard@gmail.com> >> wrote: >> >> > On Mon, 19 Jul 2021 at 18:26, Guilliam Xavier < >> guilliam.xavier@gmail.com> >> > wrote: >> > >> > > On Mon, Jul 19, 2021 at 4:26 PM Nicolas Grekas < >> nicolas.grekas@gmail.com >> > > >> > > wrote: >> > > >> > > > >> > > > https://github.com/php/php-src/pull/7259 >> > > > >> > > >> > > Great! Thanks! Interesting how it works out-of-the-box with just this >> > > addition in Zend/zend_language_parser.y: >> > > >> > > ```diff >> > > type_expr: >> > > type { $$ = $1; } >> > > | '?' type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } >> > > | union_type { $$ = $1; } >> > > | intersection_type { $$ = $1; } >> > > + | '?' intersection_type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } >> > > ; >> > > ``` >> > > >> > > On Mon, Jul 19, 2021 at 5:09 PM Dan Ackroyd <Danack@basereality.com> >> > > wrote: >> > > >> > > > nicolas-grekas wrote on the PR: >> > > > > ?X&Y cannot be confused with >> > > > >> > > > It confused me. A compiler might understand it, but as a human I >> have >> > > > trouble understanding it. >> > > > >> > > > Trowski wrote: >> > > > > The syntax should be either ?(X&Y) or (X&Y)|null >> > > > >> > > > Non-ambiguous syntax is much better than ambiguous syntax. >> > > > >> > > >> > > Maybe it's just a matter of habit? >> > > For instance I got used to seeing things like `!$x = f()` (e.g. `if >> (!$x >> > = >> > > f()) { throw /*...*/; } /* use $x */`) because some CS consider >> explicit >> > > parentheses in `!($x = f())` redundant (as PHP has a special case that >> > > "overrides" the normal precedence `(!$x) = f()` which would be an >> error). >> > > If you first consider `X&Y` as a type "unit", then it makes sense to >> make >> > > it "nullable" by prefixing it with `?`, I think? >> > > >> > > >> > > > >> > > > But this discussion is moot for 8.1. >> > > > >> > > > This limitation might make intersection types not be considered >> usable >> > > > by some projects, but the feature freeze is today. >> > > > >> > > >> > > Which can also be reversed: "The feature freeze is today, but this >> > > limitation might make intersection types not be considered usable by >> some >> > > projects"? (playing devil's advocate, I don't master the process) >> > > >> > > Regards, >> > > >> > > -- >> > > Guilliam Xavier >> > > >> > >> > Since when is usability for a specific project a consideration an RFC >> needs >> > to have? >> > If Symfony can't use it in its current state tough luck, >> > I'm sure plenty of other projects can, especially now that using 'new >> > Class' is possible as a default object value. >> > >> > I frankly don't care about being able to have some sort of partial union >> > possible with the usage of intersection types, >> > because it seems the machinery in the engine which makes null work, >> should >> > also allow any standard PHP types as those are part of a bitflag and >> > handling variance with them seems to work just fine... >> > >> > But for the love of god, this proposed syntax is horrendous, saying >> ?X&Y is >> > unambiguous is disingenuous. >> > It can either mean (?X)&Y = (X|null)&Y or ?(X&Y) = (X&Y)|null, the >> former >> > one being bogus as null&Y is an impossible type, something which should >> > error out to point out a potential bug in the same way we do for >> redundant >> > types that we know at compile time. >> > And if we allow this syntax then we really should be allowing ?A|B >> which is >> > dumb. >> > (and no ?X&Y is NOT something I consider forward compatible when it is >> just >> > going to become one more edge case we need to maintain because people >> have >> > no patience). >> > >> > If ?(X&Y) is allowed then ?(A|B) should also be allowed, and that needs >> an >> > RFC for sure due to the controversy it had in the union type RFC. >> > >> > The only remaining sensible choice is (X&Y)|null / null|(X&Y), but as I >> > said above if the machinery for null is there it must mean the machinery >> > for int/string/array/float/bool is also there, and frankly being able >> to do >> > something like (Traversable&Countable)|array is also extremely valuable, >> > maybe even more than nullability, but in any case this is going to be >> > confusing for end-users why only null (or standard PHP types) can be >> > combined in a union with intersection types. >> > >> > That's one reason why it's only pure intersection types, if I had more >> time >> > (or for crying out loud somebody would have paid me) to work on this I >> > would have loved to get full composite types working. >> > >> > And I find it frankly insulting that in the four month this RFC has been >> > published for discussion, with multiple push backs for voting due to >> bugs >> > and me wanting that people know what implementation is - for the most >> part >> > - going to land in php-src, this specific point has not been raised. >> > It just feels like you are pissing on 6+ months of *unpaid* work I did >> > because it doesn't suit your needs, and you just realised that and so >> > decided to throw a wrench into a carefully crafted RFC to "fix" it by >> using >> > a Twitter mob to put pressure on us/me. >> > >> > Maybe this topic didn't come up because for nearly everyone else "Pure >> > Intersection Types" means what it says on the can, moreso that in the >> RFC >> > the following line: >> > > This means it would *not* be possible to mix intersection and union >> types >> > together such as A&B|C, this is left as a future scope >> > makes it clear, and most voters also understood that '?' is not a type >> > "flag" but is syntactic sugar for 'null|'. >> > >> > There are plenty of issues with the implementation, from the whack >> parser >> > hack, the non support for static/parent/self, to the complexity of the >> > variance code, but I made all of those clear because they made *me* >> > uncomfortable with the implementation. >> > So if you are going to force this crappy syntax, then I'd rather axe >> this >> > feature completely (and nobody can use/play with it) then have >> something I >> > do not accept, nor are forced to accept because of a vote, land in core >> > making PHP even more of an inconsistent joke. >> > >> >> Hey George, >> >> I don't think there's been any malicious intent here -- it was obvious to >> you and I that not allowing unions implies not allowing nullability, but I >> can see how people less familiar with our type system implementation would >> not make that connection. After all, we do provide the separate ?T syntax, >> even if it is an internal alias for T|null. >> >> It's an unfortunate fact of the RFC process that concerns are sometimes >> only raised when voting starts and people start looking at the >> implementation -- or in this case, when they test the implementation after >> it has landed... >> >> In any case, I think it's pretty clear by now that there are some fairly >> strong opinions both on what the syntax for this should be (between ?X&Y, >> ?(X&Y), (X&Y)|null and X&Y|null, all of which have reasonable arguments >> for >> and against) and whether we should have this special case at all. I'm not >> sure we will be able to reach a sufficient consensus in this discussion. >> >> For that reason, I think this change should go through the RFC process. We >> can grant this an exemption from the feature freeze (as a clarification of >> an accepted proposal), but it should still go through the process. >> >
Thanks Nikita, Joe and the others for opening the possibility of a late RFC. I'm going to write one down asap! Nicolas
  115540
July 21, 2021 08:49 guilliam.xavier@gmail.com (Guilliam Xavier)
Replying to multiple things:

On Mon, Jul 19, 2021 at 8:16 PM G. P. B. banyard@gmail.com> wrote:

> > And I find it frankly insulting that in the four month this RFC has been > published for discussion, with multiple push backs for voting due to bugs > and me wanting that people know what implementation is - for the most part > - going to land in php-src, this specific point has not been raised. >
I'm sorry if you felt it like that (even if maybe not from me personally), as I had the impression that it *has* been raised, several times: - https://externals.io/message/113712#113730 : "Does this miss an opportunity, though? It's useful to be able to write A&B|null." - https://github.com/php/php-src/pull/6799#issuecomment-804761117 : "Is there any reason to not allow ?A&B type?" - https://github.com/php/php-src/pull/6799#issuecomment-805175050 : "However, if I'm not mistaken, you would create the only type that is not nullable. That feels like an unnecessary edge-case. Would it be very difficult to at least allow the union of an intersect type with null?" Even though you replied to each of them (explaining the why, so I didn't add an extra voice then*), that does look like this specific point was actually raised in early discussion? *But I can add my thank you for implementing this feature in the first place! =) On Tue, Jul 20, 2021 at 1:07 PM Nikita Popov ppv@gmail.com> wrote:
> > I don't think there's been any malicious intent here -- it was obvious to > you and I that not allowing unions implies not allowing nullability, but I > can see how people less familiar with our type system implementation would > not make that connection. After all, we do provide the separate ?T syntax, > even if it is an internal alias for T|null. > > It's an unfortunate fact of the RFC process that concerns are sometimes > only raised when voting starts and people start looking at the > implementation -- or in this case, when they test the implementation after > it has landed... >
Agreed to both points. On Tue, Jul 20, 2021 at 7:40 PM Jordan LeDoux ledoux@gmail.com> wrote:
> > For example, in my own projects if I had the need for such a thing, I > would instead use: X&Y&Optional. This would enable me to provide > arbitrarily detailed information about the conditions of the optional > nature of the data and an implementation of it that was aware of the nature > of my program. That can't be easily achieved with nulls. >
Nicolas said: "I know about the null pattern, but it is quite uncommon in PHP, because "null" works just great usually." (and I would add that it probably suffers from the lack of generics and/or tagged unions). In retrospect, do you think you would argue against https://wiki.php.net/rfc/nullsafe_operator ? and even https://wiki.php.net/rfc/nullable_types ? On Tue, Jul 20, 2021 at 9:19 PM Deleu <deleugyn@gmail.com> wrote:
> > I don't know if anyone has offered this before, but if making an exception > for nullable Intersection is on the table, what about putting the null sign > at the end? > > X&Y&Z? > > It doesn't seem ambiguous because PHP defines ?X as nullable X. It doesn't > seem obvious what the ? is doing at the end but at least it doesn't seem > ambiguous. > >> IIRC one reason the prefix syntax was chosen (for PHP) instead of the
postfix one (from other languages) is to prevent a potential conflict with hypothetical future generics using a "X" syntax (because "X" would then have been interpreted as a PHP closing tag). On Wed, Jul 21, 2021 at 9:05 AM Nicolas Grekas grekas@gmail.com> wrote:
> > Thanks Nikita, Joe and the others for opening the possibility of a late > RFC. I'm going to write one down asap! >
Thanks, looking forward to the RFC.
  115537
July 20, 2021 18:35 me@kelunik.com (Niklas Keller)
> > > > > nicolas-grekas wrote on the PR: > > > > > ?X&Y cannot be confused with > > > > > > > > It confused me. A compiler might understand it, but as a human I have > > > > trouble understanding it. >
I think ?X&Y would be a pretty poor syntax choice, as it's ambiguous as many others said: (X|null)&(Y) vs. (X&Y)|null. What hasn't been proposed, yet, is ?X&?Y, so requiring either all or none of the types being nullable. Best, Niklas
  115538
July 20, 2021 19:19 deleugyn@gmail.com (Deleu)
On Tue, Jul 20, 2021, 20:35 Niklas Keller <me@kelunik.com> wrote:

> > > > > > > nicolas-grekas wrote on the PR: > > > > > > ?X&Y cannot be confused with > > > > > > > > > > It confused me. A compiler might understand it, but as a human I > have > > > > > trouble understanding it. > > > > I think ?X&Y would be a pretty poor syntax choice, as it's ambiguous as > many others said: (X|null)&(Y) vs. (X&Y)|null. > > What hasn't been proposed, yet, is ?X&?Y, so requiring either all or none > of the types being nullable. > > Best, > Niklas >
From the look of the syntax I would prefer it not to land on 8.1 at all, which gives us another year to think of any syntax less aggressive to the eyes. One of the things I loved about the Pure Intersection RFC was that it avoided the discussion around parenthesis on type declaration altogether. Maybe if a type alias is proposed before mingling Union and Intersection, then it's less of an aggressive syntax. Type T = (X&Y)|Z|null I don't know if anyone has offered this before, but if making an exception for nullable Intersection is on the table, what about putting the null sign at the end? X&Y&Z? It doesn't seem ambiguous because PHP defines ?X as nullable X. It doesn't seem obvious what the ? is doing at the end but at least it doesn't seem ambiguous.
>
  115536
July 20, 2021 17:40 jordan.ledoux@gmail.com (Jordan LeDoux)
On Mon, Jul 19, 2021 at 11:16 AM G. P. B. banyard@gmail.com> wrote:

> On Mon, 19 Jul 2021 at 18:26, Guilliam Xavier xavier@gmail.com> > wrote: > > > On Mon, Jul 19, 2021 at 4:26 PM Nicolas Grekas grekas@gmail.com > > > > wrote: > > > > > > > > https://github.com/php/php-src/pull/7259 > > > > > > > Great! Thanks! Interesting how it works out-of-the-box with just this > > addition in Zend/zend_language_parser.y: > > > > ```diff > > type_expr: > > type { $$ = $1; } > > | '?' type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } > > | union_type { $$ = $1; } > > | intersection_type { $$ = $1; } > > + | '?' intersection_type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } > > ; > > ``` > > > > On Mon, Jul 19, 2021 at 5:09 PM Dan Ackroyd <Danack@basereality.com> > > wrote: > > > > > nicolas-grekas wrote on the PR: > > > > ?X&Y cannot be confused with > > > > > > It confused me. A compiler might understand it, but as a human I have > > > trouble understanding it. > > > > > > Trowski wrote: > > > > The syntax should be either ?(X&Y) or (X&Y)|null > > > > > > Non-ambiguous syntax is much better than ambiguous syntax. > > > > > > > Maybe it's just a matter of habit? > > For instance I got used to seeing things like `!$x = f()` (e.g. `if (!$x > = > > f()) { throw /*...*/; } /* use $x */`) because some CS consider explicit > > parentheses in `!($x = f())` redundant (as PHP has a special case that > > "overrides" the normal precedence `(!$x) = f()` which would be an error). > > If you first consider `X&Y` as a type "unit", then it makes sense to make > > it "nullable" by prefixing it with `?`, I think? > > > > > > > > > > But this discussion is moot for 8.1. > > > > > > This limitation might make intersection types not be considered usable > > > by some projects, but the feature freeze is today. > > > > > > > Which can also be reversed: "The feature freeze is today, but this > > limitation might make intersection types not be considered usable by some > > projects"? (playing devil's advocate, I don't master the process) > > > > Regards, > > > > -- > > Guilliam Xavier > > > > Since when is usability for a specific project a consideration an RFC needs > to have? > If Symfony can't use it in its current state tough luck, > I'm sure plenty of other projects can, especially now that using 'new > Class' is possible as a default object value. > > I frankly don't care about being able to have some sort of partial union > possible with the usage of intersection types, > because it seems the machinery in the engine which makes null work, should > also allow any standard PHP types as those are part of a bitflag and > handling variance with them seems to work just fine... > > But for the love of god, this proposed syntax is horrendous, saying ?X&Y is > unambiguous is disingenuous. > It can either mean (?X)&Y = (X|null)&Y or ?(X&Y) = (X&Y)|null, the former > one being bogus as null&Y is an impossible type, something which should > error out to point out a potential bug in the same way we do for redundant > types that we know at compile time. > And if we allow this syntax then we really should be allowing ?A|B which is > dumb. > (and no ?X&Y is NOT something I consider forward compatible when it is just > going to become one more edge case we need to maintain because people have > no patience). > > If ?(X&Y) is allowed then ?(A|B) should also be allowed, and that needs an > RFC for sure due to the controversy it had in the union type RFC. > > The only remaining sensible choice is (X&Y)|null / null|(X&Y), but as I > said above if the machinery for null is there it must mean the machinery > for int/string/array/float/bool is also there, and frankly being able to do > something like (Traversable&Countable)|array is also extremely valuable, > maybe even more than nullability, but in any case this is going to be > confusing for end-users why only null (or standard PHP types) can be > combined in a union with intersection types. > > That's one reason why it's only pure intersection types, if I had more time > (or for crying out loud somebody would have paid me) to work on this I > would have loved to get full composite types working. > > And I find it frankly insulting that in the four month this RFC has been > published for discussion, with multiple push backs for voting due to bugs > and me wanting that people know what implementation is - for the most part > - going to land in php-src, this specific point has not been raised. > It just feels like you are pissing on 6+ months of *unpaid* work I did > because it doesn't suit your needs, and you just realised that and so > decided to throw a wrench into a carefully crafted RFC to "fix" it by using > a Twitter mob to put pressure on us/me. > > Maybe this topic didn't come up because for nearly everyone else "Pure > Intersection Types" means what it says on the can, moreso that in the RFC > the following line: > > This means it would *not* be possible to mix intersection and union types > together such as A&B|C, this is left as a future scope > makes it clear, and most voters also understood that '?' is not a type > "flag" but is syntactic sugar for 'null|'. > > There are plenty of issues with the implementation, from the whack parser > hack, the non support for static/parent/self, to the complexity of the > variance code, but I made all of those clear because they made *me* > uncomfortable with the implementation. > So if you are going to force this crappy syntax, then I'd rather axe this > feature completely (and nobody can use/play with it) then have something I > do not accept, nor are forced to accept because of a vote, land in core > making PHP even more of an inconsistent joke. > > Best regards, > > George P. Banyard >
Hey George,
> But for the love of god, this proposed syntax is horrendous, saying ?X&Y is
unambiguous is disingenuous.
> It can either mean (?X)&Y = (X|null)&Y or ?(X&Y) = (X&Y)|null, the former one being bogus as null&Y is an impossible type, something which should
error out to point out a potential bug in the same way we do for redundant types that we know at compile time. It is ambiguous from a syntax perspective, but wouldn't it be rather clear from an intent perspective? As you note, null&Y is sort of semantically nonsense, as if someone did is_array() && is_bool(). I think this is what people have been meaning when they say it's unambiguous. Personally, I think intersection types are very useful even without nullability. Since intersection types only make sense with objects in the first place, the objects themselves should be able to provide better information about correctness than a simple null can, and such an effect could be achieved with a fairly trivial interface that maintains a single flag. I'm not entirely convinced that nullability on its own provides something that significantly improves the accepted RFC, except to support design patterns that have been used in the absence of this feature. Any situation in which I might want to use ?X&Y that I can think of would be better designed code if I instead guaranteed a state-aware object that I can interrogate. For example, in my own projects if I had the need for such a thing, I would instead use: X&Y&Optional. This would enable me to provide arbitrarily detailed information about the conditions of the optional nature of the data and an implementation of it that was aware of the nature of my program. That can't be easily achieved with nulls. I don't deny that ?X&Y would be used if it were available, but I wonder if this actually encourages code which is unnecessarily ambiguous itself. What it truly enables from my perspective is the ability to delay checking for violations of an expectation in a program until later in the code, and in this case I wonder if allowing that delay would simply enable implementations that are fundamentally worse. Any code which accepts ?X&Y would know less about the construction and configuration of the object than the code that actually constructed and configured it, and any code that returns a similar type would be reducing information about the state. In short, I don't see how this actually improves the language unless it addresses the full issue of mixing union and intersection types, which was definitely beyond the scope of the RFC. Jordan