Union `&` operator

php.internals

[ ]

5 years ago
When you follow ISP, you probably would have a lot of interfaces. Thus, client code may require an object to implement a bunch of interfaces depending on functionality needed. Consider class `Bar`: ```php interface A {} interface B {} interface C {} interface D {} interface E {} class Bar implements A, B, C, D, E { // } ``` And some code, which uses `Bar`, but depends rather on interfaces, which functionality is actually needed: ```php function foo(A & B & E $object) { // some work var_dump($object); } ``` It is currently feasable like this (ugly): ```php function foo(A $object) { (function(B $object) { (function(E $object) { // some work var_dump($object); })($object); })($object); } ``` Or like this (more readable, but still): ```php function foo(A | B | E $object) { if (!$object instanceof A) { throw new \RuntimeException(); } if (!$object instanceof B) { throw new \RuntimeException(); } if (!$object instanceof E) { throw new \RuntimeException(); } // some work var_dump($object); } ``` Another possible option is to create interface, which combines `A`, `B`, `E`, but in some cases it would lead us to interfaces explosion. This idea proposes to eliminate `instanceof` checks by introducing `&` union operator.

Olle Härstedt

5 years ago
2020-11-07 15:12 GMT, Eugene Sidelnyk <zsidelnik@gmail.com>:
> When you follow ISP, you probably would have a lot of interfaces. > Thus, client code may require an object to implement a bunch of interfaces > depending on functionality needed. > > Consider class `Bar`: > > ```php > > interface A {} > > interface B {} > > interface C {} > > interface D {} > > interface E {} > > class Bar implements A, B, C, D, E { > // > } > ``` > > > And some code, which uses `Bar`, but depends rather on interfaces, which > functionality is actually needed: > > ```php > function foo(A & B & E $object) { > // some work > > var_dump($object); > } > ``` > > It is currently feasable like this (ugly): > > ```php > function foo(A $object) { > (function(B $object) { > > (function(E $object) { > > // some work > > var_dump($object); > > })($object); > > })($object); > } > ``` > > Or like this (more readable, but still): > > ```php > function foo(A | B | E $object) { > > if (!$object instanceof A) { > throw new \RuntimeException(); > } > > if (!$object instanceof B) { > throw new \RuntimeException(); > } > > if (!$object instanceof E) { > throw new \RuntimeException(); > } > > // some work > > var_dump($object); > } > ``` > > Another possible option is to create interface, which combines `A`, `B`, > `E`, but in some cases it would lead us to interfaces explosion. > > This idea proposes to eliminate `instanceof` checks by introducing `&` > union operator. >
You mean intersections? Psalm supports this notation.

[ ]

5 years ago
Yes. And I think it will be good if enforced by language. On Sat, Nov 7, 2020, 5:33 PM Olle Härstedt <olleharstedt@gmail.com> wrote:

Olle Härstedt

5 years ago
https://wiki.php.net/rfc/intersection_types 2020-11-07 16:37 GMT+01:00, Eugene Sidelnyk <zsidelnik@gmail.com>:

Sara Golemon

5 years ago
On Sat, Nov 7, 2020 at 9:33 AM Olle Härstedt <olleharstedt@gmail.com> wrote:
> 2020-11-07 15:12 GMT, Eugene Sidelnyk <zsidelnik@gmail.com>: > > function foo(A & B & E $object) { > > // some work > > var_dump($object); > > } > > > > You mean intersections? Psalm supports this notation. > >
IIRC we discussed intersection data types when union types were on the table and we settled on a "take it slow" position. That said, union types are... **checks notes**... oh, wow. Actually still pretty new (introduced in 8.0). My conservative side wants to wait and see what the community does with unions, but if static analyzers are already providing this functionality, then maybe the community has already made its position known. Questions to answer in any RFC on this topic: 1. Do we support complex types combining unions with intersections? e.g. function foo(((countable&traversable) | (stringable|jsonserializable)) $obj) { ... } Pros: Super descriptive Cons: Super ugly My opinion: Maybe, but not immediately. Let simple unions/intersections bake for a version or two. 2. Do we also bring in type alliasing? e.g. type CountableTraversable = Countable & Traversable; type AllTheSerializations = Stringable & JSONSerializable; function foo(CountableTraversable | AllTheSerializations $obj) { ... } Pros: Can make a given code base more readable once you know the types Cons: Can make figuring out types in a new codebase harder. My opinion: Yes. Sooner the better. 3. How does this intersect (pun!) with coersion when strict_types=0 ? Initial thoughts: Probably very poorly. 4. Should we be considering other bloat^W features? a. Exclusive Union: type SingleSerialization = Stringable ^ JSONSerializable; // XOR: Must be one, but not both b. Blacklist: type AnythingButDOM = !DOMElement; My opinion: Hell no. Just spitballin'. -Sara

Larry Garfield

5 years ago
On Sat, Nov 7, 2020, at 2:47 PM, Sara Golemon wrote:
> On Sat, Nov 7, 2020 at 9:33 AM Olle Härstedt <olleharstedt@gmail.com> wrote: > > > 2020-11-07 15:12 GMT, Eugene Sidelnyk <zsidelnik@gmail.com>: > > > function foo(A & B & E $object) { > > > // some work > > > var_dump($object); > > > } > > > > > > > You mean intersections? Psalm supports this notation. > > > > > IIRC we discussed intersection data types when union types were on the > table and we settled on a "take it slow" position. That said, union types > are... **checks notes**... oh, wow. Actually still pretty new (introduced > in 8.0). > > My conservative side wants to wait and see what the community does with > unions, but if static analyzers are already providing this functionality, > then maybe the community has already made its position known.
I would be very in favor of intersection types. It's not really useful for primitives, but with interfaces it becomes quite handy.
> Questions to answer in any RFC on this topic: > 1. Do we support complex types combining unions with intersections? > e.g. function foo(((countable&traversable) | (stringable|jsonserializable)) > $obj) { ... } > Pros: Super descriptive > Cons: Super ugly > My opinion: Maybe, but not immediately. Let simple unions/intersections > bake for a version or two.
I'd favor a type being exclusively unions or intersections. No mixing, with the possible exception of allowing the type to be nullable. So Foo & Bar is OK, Bar | Baz is OK, but Foo & Bar | Baz is just more complex than any reasonable person should be doing.
> 2. Do we also bring in type alliasing? > e.g. > type CountableTraversable = Countable & Traversable; > type AllTheSerializations = Stringable & JSONSerializable; > function foo(CountableTraversable | AllTheSerializations $obj) { ... } > Pros: Can make a given code base more readable once you know the types > Cons: Can make figuring out types in a new codebase harder. > My opinion: Yes. Sooner the better.
This is a separate RFC from intersection types and should be submitted separately. That said, yes please!
> 3. How does this intersect (pun!) with coersion when strict_types=0 ? > Initial thoughts: Probably very poorly.
I'm unclear how/why it would? Please explain?
> 4. Should we be considering other bloat^W features? > a. Exclusive Union: type SingleSerialization = Stringable ^ > JSONSerializable; // XOR: Must be one, but not both > b. Blacklist: type AnythingButDOM = !DOMElement; > My opinion: Hell no. Just spitballin'. > > -Sara
Hard pass. At that point, make separate methods. --Larry Garfield

[ ]

5 years ago
> 1. Do we support complex types combining unions with intersections?
We can do it at any time, so we should not hurry with this. If we think about why people would like to do such complex types - probably it is because PHP currently doesn't support methods overloading. Thus we'd rather introduce methods overloading than complex union structures, but with yet that is a different topic.
> 2. Do we also bring in type aliasing?
Think that would be great (class can define some types, which it uses, so that client code can type-hint it): ```php class Foo { public type Collection = Countable & Traversable; public function doThisAndThat(self::Collection $collection) { // ... } } class Bar { public function delegateToFoo(Foo::Collection $collection) { $this->foo->doThisAndThat($collection); } } ``` But, as Larry told, it should be a different RFC.
> 3. How does this intersect (pun!) with coercion when strict_types=0 ?
If we don't introduce complex structures (I hope), it should work the same way independently of whether strict_types is 0 or 1.
> 4. Should we be considering other bloat^W features?
Not really. There should be some case where such features are actually needed. Regards, Eugene On Sat, Nov 7, 2020 at 10:47 PM Sara Golemon <pollita@php.net> wrote:

Marco Pivetta

5 years ago
Hey Eugene, On Sat, Nov 7, 2020 at 4:13 PM Eugene Sidelnyk <zsidelnik@gmail.com> wrote:
> ```php > function foo(A & B & E $object) { > // some work > > var_dump($object); > } > ``` >
Fully support this for parameter, property and return types: already making good use of intersection types since a few years. As for when to allow them: as others have suggested in this thread, keep it simple for now, and only allow intersection of class/interface references. If you want to raise an RFC, special care has to be taken for the variance in inheritance semantics. Greets, Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/

[ ]

5 years ago
> As for when to allow them: as others have suggested in this thread, keep
it simple for now, and only allow intersection of class/interface references. Yes, I think that single intersection type should support just one class and any/or any number of interfaces with the condition that they don't collide with one another.
> If you want to raise an RFC, special care has to be taken for the
variance in inheritance semantics. I don't think neither that I am able to consider all possible impacts to write the RFC nor implement this, but Levi Morrison (levim@php.net) can. On Sun, Nov 8, 2020 at 4:53 PM Marco Pivetta <ocramius@gmail.com> wrote:

[ ]

5 years ago
Levi will not have time for this. Who else do you suggest? On Sun, Nov 8, 2020, 6:47 PM Eugene Sidelnyk <zsidelnik@gmail.com> wrote:

Girgias

5 years ago
On Sat, 14 Nov 2020 at 11:17, Eugene Sidelnyk <zsidelnik@gmail.com> wrote:
> Levi will not have time for this. Who else do you suggest? >
Please do NOT prompt someone to implement a feature for you for free. Many of us would like intersection types but it takes time to implement it. And as a reminder most people who work on php-src do so on their free time with no compensation whatsoever. I recommend you read this article from Christopher Jones from Oracle about the PHP RFC process: https://blogs.oracle.com/opal/the-mysterious-php-rfc-process-and-how-you-can-change-the-web Now, if you are interested in implementing it yourself there are various resources available which we would be happy to link you. Best, George P. Banyard PS: Again, please do not top post as it is one of the guidelines of this mailing list, see: https://github.com/php/php-src/blob/master/docs/mailinglist-rules.md

[ ]

5 years ago
I am wondering why don't you create some kind of donation system? So that customers (end programmers) can vote for features by means of their money. In russian it is known as "Голосуй рублём". This will give the community an ability to provide feedback of what features they like or don't. BTW, this way people won't be able to cheat inasmuch vote is an monetary unit On Sat, Nov 14, 2020, 2:56 PM G. P. B. <george.banyard@gmail.com> wrote: