[PSR-5] Intersection Types

325 views
Skip to first unread message

Chuck Burgess

unread,
Oct 19, 2018, 8:50:34 AM10/19/18
to PHP Framework Interoperability Group
Hello everyone,

There is a new PR [1] from a contributor, asking for an Intersection Type Operator.  This appears to simply use `&` akin to how `|` is used for Union Types.

Neither Unions nor Intersections are (yet) in the language itself, but `string|null` Union Typing in Tags has been in wide usage for a while now.  In looking over RFCs on attempts to get these two Type Operators into the language, it seems likely to me that the Operators chosen will be `|` and `&` if they do ever get in.  As such, I'm personally good with the choice of `&` for Intersection Operator for Typing in Tags.

Please keep discussion on this request on this ML thread.

Chuck Burgess, Editor

Johannes Schmitt

unread,
Oct 20, 2018, 6:53:57 AM10/20/18
to php...@googlegroups.com
Hi there,

generally, I think the addition of `&` is a great idea.

One thing regarding the grammar specifically, right now you would support mixing `|` with `&` like `A|B&C`. I'm not sure if mixing would be desirable (I don't have use-case for this at this point). Also the grammar currently requires `|` to be before `&`- so something like `A&B|C` would not be supported - I'm not sure if this is intentional? Maybe it's best to only either support `|` or `&`, but not allow to mix them for the moment?

Thanks,
Johannes

--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/9f5897a1-b771-4352-8cba-9851bddcb9ce%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

AzJezz

unread,
Oct 20, 2018, 6:43:21 PM10/20/18
to php...@googlegroups.com
Hi,

i find it quite confusing myself, here's a use case mixing `&` and `|`.

```php
<?php

namespace App\Foo;

class Bar {
    /**
    * in this example `Bar` constructor accepts an object that implement
    * ( `CacheInterface` and `ResetableInterface` ) or `ResetableCacheInterface`
    *
    * should the doc block be formatted this way :
    *
    * @param object&CacheInterface&ResetableInterface|ResetableCacheInterface $cache
    *
    * or maybe like this ?
    *
    * @param object&ResetableCacheInterface|ResetableInterface&CacheInterface $cache
    *
    * or maybe we can use parentheses ? ¯\_(ツ)_/¯
    *
    * @param object & ( ResetableCacheInterface | ( ResetableInterface & CacheInterface ) ) $cache
    */
    public function __construct($cache) {
        // initialize cache property
    }
}
```


Chuck Burgess

unread,
Oct 20, 2018, 9:22:02 PM10/20/18
to php...@googlegroups.com
Yes, parentheses would be required for controlling order of precedence.

In your example, I would expect:


* @param (CacheInterface&ResetableInterface)|ResetableCacheInterface $cache


CRB


php...@jantvrdik.com

unread,
Oct 21, 2018, 10:40:31 AM10/21/18
to php...@googlegroups.com
Regarding the ABNF grammar, there are few things that need to be decided.

(1) Priority / interaction with union and array "operators". I would strongly recommend disallowing union and intersection on the same "level" and always require brackets to explicitly declare the intention. For consistency with union types, array and intersection should be allowed on the same level with array having higher priority. This matches behavior used by PHPStan and can be achieved for example with the following grammar

type = atomic [union / intersection]
union = 1*("|" atomic)
intersection = 1*("&" atomic)
atomic = identifier [array] / "(" type ")" [array]
array = 1*("[]")
identifier = keyword / class-name
keyword = "array" / "bool" / ...
class-name = ["\"] label *("\" label)
label = label-head *label-tail
label-head = ALPHA / "_" / %x80-FF
label-tail = ALPHA / DIGIT / "_" / %x80-FF


(2) Allowing horizontal whitespaces around operators. With more complex types it makes sense to allow horizontal whitespaces around operators. It complicates the grammar a bit, but it makes complex types a lot readable. It may be better the post allowing horizontal whitespaces as a standalone PR independent of intersection types.

Regards,
Jan Tvrdík



---------- Původní e-mail ----------

Od: Chuck Burgess <demon...@gmail.com>

Komu: php...@googlegroups.com

Datum: 21. 10. 2018 3:22:06

Předmět: Re: [PSR-5] Intersection Types
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/CANsgjnuPwyOhEGg5c2AWv%3D%2BABSjGrVVcuvrzZS-O7YTWibDUeQ%40mail.gmail.com.

Chuck Burgess

unread,
Oct 23, 2018, 9:09:19 AM10/23/18
to php...@googlegroups.com
Having both operators at different levels would mean that combinations such as `@param (A|B)&C $test` would have to be written as `@param A&C|B&C $test`.

I'm not against allowing whitespace around the operators, if the implementors agree it's still easy enough to parse correctly.  Since the variable name should come between the Types and the Description, perhaps that's not a big deal.

Alexey Gopachenko

unread,
Oct 24, 2018, 9:58:07 AM10/24/18
to PHP Framework Interoperability Group
PhpStorm always allowed spaces around operators, but will never generate them.

Matthew Brown

unread,
Oct 26, 2018, 8:47:11 PM10/26/18
to PHP Framework Interoperability Group
Psalm and PHPStan allow spaces too

Chuck Burgess

unread,
Nov 8, 2018, 1:52:18 PM11/8/18
to PHP Framework Interoperability Group
Previous replies indicate that whitespace around operators is perfectly acceptable, so that seems resolved.

What about the issue of operator precedence when "|" and "&" are both needed?  

Do we want to say one is higher order than the other, resulting in the possibility of one Type being listed multiple times:  @return A & C   |   B & C

Or should they be equal precedence, needing parentheses to enforce order:  @return (A | B) & C

CRB
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+unsubscribe@googlegroups.com.


To post to this group, send email to php...@googlegroups.com.

To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/9f5897a1-b771-4352-8cba-9851bddcb9ce%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.







--

You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.

To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+unsubscribe@googlegroups.com.


To post to this group, send email to php...@googlegroups.com.

To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/CANdv-DeCnvpSgfQC9WP_kq9yz%3DFq4O8tfQok_o%2BoGR4QNyg1Ww%40mail.gmail.com.

For more options, visit https://groups.google.com/d/optout.







--

You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.

To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+unsubscribe@googlegroups.com.


To post to this group, send email to php...@googlegroups.com.

To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/CAMshokcbDnAkcMqAqm%3DeTG52oUFgXXz%2B5emTckN0iib1tj%2BEbQ%40mail.gmail.com.

For more options, visit https://groups.google.com/d/optout.









--

You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.

To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+unsubscribe@googlegroups.com.


To post to this group, send email to php...@googlegroups.com.

To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/CANsgjnuPwyOhEGg5c2AWv%3D%2BABSjGrVVcuvrzZS-O7YTWibDUeQ%40mail.gmail.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+unsubscribe@googlegroups.com.

Larry Garfield

unread,
Nov 9, 2018, 1:10:35 PM11/9/18
to php...@googlegroups.com
Honestly, I'm skeptical that we want to develop a grammar for full algebraic
type definitions in docs when the language doesn't support it. That risks
encouraging some very bad practices, and if PHP itself ever adds native
algebraic types it could get weird if there's any inconsistency (which there
almost certainly will be).

That said, if we must support it in the doc format then follow the same
precedence order as PHP itself: & binds higher than |. Having a different set
of parse rules than PHP itself is the way to madness. :-)

--Larry Garfield

On Thursday, November 8, 2018 12:52:18 PM CST Chuck Burgess wrote:
> Previous replies indicate that whitespace around operators is perfectly
> acceptable, so that seems resolved.
>
> What about the issue of operator precedence when "|" and "&" are both
> needed?
>
> Do we want to say one is higher order than the other, resulting in the
> possibility of one Type being listed multiple times: @return A & C | B
> & C
>
> Or should they be equal precedence, needing parentheses to enforce order:
> @return (A | B) & C
>
> CRB
>
> On Tuesday, October 23, 2018 at 8:09:19 AM UTC-5, Chuck Burgess wrote:
> > Having both operators at different levels would mean that combinations
> > such as `@param (A|B)&C $test` would have to be written as `@param A&C|B&C
> > $test`.
> >
> > I'm not against allowing whitespace around the operators, if the
> > implementors agree it's still easy enough to parse correctly. Since the
> > variable name should come between the Types and the Description, perhaps
> > that's not a big deal.
> >
> > CRB
> > *about.me/ashnazg <http://about.me/ashnazg>*
signature.asc

Chuck Burgess

unread,
Nov 9, 2018, 1:15:42 PM11/9/18
to php...@googlegroups.com
Ah, I had assumed they were same precedence in PHP... it's obviously been way too long since I've seen them in code.

I agree with following PHP here, then, as Larry described... @return A & C   |   B & C

--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.

To post to this group, send email to php...@googlegroups.com.

Matthew Brown

unread,
Nov 9, 2018, 6:14:00 PM11/9/18
to php...@googlegroups.com
I agree - I don’t think we want to encourage types that expand into monstrosities once the brackets have been evaluated out. Better to disallow (...) & C.
You received this message because you are subscribed to a topic in the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/php-fig/W1VyAtoqGQ8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to php-fig+u...@googlegroups.com.

To post to this group, send email to php...@googlegroups.com.

Alexey Gopachenko

unread,
Nov 12, 2018, 10:37:21 AM11/12/18
to php...@googlegroups.com
Actually I'm just so happy to see that you guys reached this conclusion - I was preparing a big post with some statistics about how hard we must work to deliver something so controversial and that it will take a HUGE toll on everyone even not using the stuff - both from point of making a compliant tool will be HARD and that tool (eg PhpStorm or CI analyzer) will be eating everyone's resources to just plug the holes in design. Phew.

Let's allow only ONE type of operator - "|" or "&" and no braces.
This will allow us to have working solutions most for real-world issues.

I have another proposal in the queue - on taking care making entire syntax friendly to graceful degradation, but this requires some more work...


Regards, Alex



Chuck Burgess

unread,
Nov 12, 2018, 10:56:15 AM11/12/18
to php...@googlegroups.com
Ok, so we have some level of consensus on allowing both operators, but having & take precedence over | rather than needing parens if they were equal precedence.


Alexey, you suggest that the two cannot be used together at all... is the &-before-| option not workable from your perspective?

Alexey Gopachenko

unread,
Nov 12, 2018, 12:07:48 PM11/12/18
to php...@googlegroups.com
>Alexey, you suggest that the two cannot be used together at all... is the &-before-| option not workable from your perspective?
Correct. Type inference/symbolic computation engine is what I do most of the time all these years -  IMO having ANY real precedence mechanism is basically the same (too much work for too few cases with huge penalty). Just omitting the braces there is no relief.


Regards, Alex


Miguel Rosales

unread,
Nov 13, 2018, 5:48:20 PM11/13/18
to PHP Framework Interoperability Group
A bit late to the party, but just wanted to give a big +1 to Larry's comment above - I don't think this feature should be defined in the docs when the language itself doesn't allow it, and afaik (may be wrong) there's no evidence that it will be supported in the future...

Probably my opinion is influenced by the fact I personally don't like union/intersection types - I think in many cases you can avoid them using interfaces and/or a bit of refactoring, and if not, there are other features that could solve the same problem in better ways (imho anyway) e.g. method overloading or even "scalar objects". Adding full support for this in a PSR could definitely influence the community in one direction that may not the best one...

As a side note, there was a thread about adding more support to generics where it was said that that's a language-specific feature out of scope of the PSR (which makes perfect sense), and this sounds quite similar to me :)

- I've used the OR | operator a lot, but since PHP7, I always try to rely less on phpdoc for typing, and more on the language itself, and in may cases when I need to add an OR, I consider a refactoring instead.

Miguel Rosales

unread,
Nov 13, 2018, 5:57:30 PM11/13/18
to PHP Framework Interoperability Group
(sorry, the last line in my previous message was meant to be removed - clumsy guys like me really need editable messages :)

Matthew Brown

unread,
Nov 13, 2018, 9:09:04 PM11/13/18
to php...@googlegroups.com
I agree that expressions like (A | B) & C should probably not be allowed, but I disagree that we should refrain, in principle, from allowing people to type their code in docblocks more expressively than PHP allows (in supported syntax).

I like that docblocks can go further - it means not having to add runtime checks, while having a reasonable degree of confidence (with static analysis tools) that your code will work.

--
You received this message because you are subscribed to a topic in the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/php-fig/W1VyAtoqGQ8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.

Chuck Burgess

unread,
Nov 16, 2018, 2:37:59 PM11/16/18
to php...@googlegroups.com
I went ahead and merged in the open PR to include Intersection Types in the draft.  This PR did not indicate anything with regard to order of precedence, so anything that develops from this ML thread can be added via a new PR.


You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.

To post to this group, send email to php...@googlegroups.com.

cyrilv...@free.fr

unread,
Nov 21, 2018, 7:44:30 AM11/21/18
to PHP Framework Interoperability Group
Hi everyone,

I have posted this PR and i am sorry i could not participate in this discussion earlier.

@CRB, thanks for accepting the PR.

The main reason i did this PR is because PHPUnit adds some methods to a mock object and phpstan-phpunit seems to correctly represent the resulting object using the intersection operator (&).
I understand though that having a full algebraic description type may lead to complex syntaxes, hard to read codes and hard to create parsers.
I am all for following PHP as much as possible.
For now, Alexey's proposition will suffice to me :

Let's allow only ONE type of operator - "|" or "&" and no braces.
So the following examples would be allowed :
@var null|int $foo
@var Foo&\PHPUnit\Framework\MockObject\MockObject $foo

But not :
@var null|Foo&Bar $object

I am not very familliar with the ABNF grammar, so feel free to correct me.

Matthew Brown

unread,
Nov 23, 2018, 10:55:56 AM11/23/18
to php...@googlegroups.com
One problem with allowing just one of & or |

The type \Mockery\MockInterface&\Foo\Bar|null should be valid IMO

--
You received this message because you are subscribed to a topic in the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/php-fig/W1VyAtoqGQ8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.

Chuck Burgess

unread,
Nov 23, 2018, 10:59:46 AM11/23/18
to php...@googlegroups.com
Yep... that's why I like Larry's suggestion to apply & before | when both are used.
CRB

You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.

To post to this group, send email to php...@googlegroups.com.

Matthew Brown

unread,
Nov 23, 2018, 11:36:39 AM11/23/18
to php...@googlegroups.com

Jan Tvrdík

unread,
Dec 7, 2018, 1:31:25 PM12/7/18
to PHP Framework Interoperability Group
The old phpDocumentor documentation already allows for `(A|B)[]`. Dropping support for parentheses entirely would break some real-world code and (as mentioned by Alexey Gopachenko) does not actually simplify anything. Supporting parentheses for some (but not all) operators would be just confusing for users. Also parentheses (in general) are very important to make more complex type expressions readable.

Allowing only one of `|` or `&` operators would break support for nullable types at minimum.

The only thing that can be maybe done, if you want to restrict the syntax somehow, is to disallow union types as operands of intersection types, i.e. to allow `(A & B) | C` but not `(A | B) & C`.

Allowing `A & B | C` as a shorter way to write `(A & B) | C` can also be done, but it seems to me like an unnecessary complication. PHPStan currently only supports the version with explicit parentheses and I don't remember any user complaining about this restriction. Also such restriction can always be easily lifted in the future, but cannot be added retrospectively.
Reply all
Reply to author
Forward
0 new messages