Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:79172 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 6579 invoked from network); 25 Nov 2014 18:03:07 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 25 Nov 2014 18:03:07 -0000 Authentication-Results: pb1.pair.com smtp.mail=php@tutteli.ch; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=php@tutteli.ch; sender-id=pass Received-SPF: pass (pb1.pair.com: domain tutteli.ch designates 80.74.154.78 as permitted sender) X-PHP-List-Original-Sender: php@tutteli.ch X-Host-Fingerprint: 80.74.154.78 ns73.kreativmedia.ch Linux 2.6 Received: from [80.74.154.78] ([80.74.154.78:35663] helo=hyperion.kreativmedia.ch) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 14/CA-40624-854C4745 for ; Tue, 25 Nov 2014 13:03:06 -0500 Received: (qmail 20790 invoked from network); 25 Nov 2014 19:03:01 +0100 Received: from cm56-153-252.liwest.at (HELO RoLaptop) (86.56.153.252) by ns73.kreativmedia.ch with ESMTPSA (AES256-SHA encrypted, authenticated); 25 Nov 2014 19:03:01 +0100 To: "'Levi Morrison'" , "'internals'" References: In-Reply-To: Date: Tue, 25 Nov 2014 19:02:58 +0100 Message-ID: <003701d008da$0c60d630$25228290$@tutteli.ch> MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Mailer: Microsoft Outlook 14.0 Thread-Index: AQMt6T151a+jokZr5JH5+SaPc1mB5Jm13fLA Content-Language: de-ch Subject: AW: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking From: php@tutteli.ch ("Robert Stoll") > -----Urspr=C3=BCngliche Nachricht----- > Von: morrison.levi@gmail.com [mailto:morrison.levi@gmail.com] Im = Auftrag von Levi Morrison > Gesendet: Dienstag, 25. November 2014 18:09 > An: internals > Betreff: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking >=20 > Dear Internals, >=20 > As you may remember, I closed the voting on the return types > [RFC](https://wiki.php.net/rfc/returntypehinting) because of a design = flaw that was found during the voting process. The > purpose of this discussion thread is to explain the various options = for checking return type compatibility with parent > methods that I think are viable, to show some of the benefits and = drawbacks of each option, and to solicit feedback on > which options you prefer and why. As such, it's much longer than a = normal message I would send. >=20 > The following code demonstrates the design flaw mentioned above: >=20 > class A { > function foo(): B { return new B; } > } >=20 > class B extends A { > function foo(): C { return new C; } > } >=20 > class C extends B {} >=20 > $b =3D new B; > $c =3D $b->foo(); >=20 > I've also used it because it can adequately show the differences in = how each of the following options work: >=20 > 1. Do covariant return types; check them at definition time > 2. Do covariant return types; check them at runtime > 3. Do invariant return types; check them at definition time >=20 > Option 1: Covariant return types with definition time checking > -------------------------------------------------------------- > This is the option that is currently implemented in my pull request. > This option catches all return type variance issues whether code is = used or not; if it got included it is checked. This means > return type variance issues can't bite you later. >=20 > When class B is defined, the engine needs to check that the method = `B::foo` has a compatible return type with `A::foo`, in > this case that equates to checking that C is compatible with type B. = This would trigger autoloading for C but it will fail in this > case because C is defined in the same file. Note that there are ways = we could fix this issue, but not reliably because of > conditionally defined classes (defining classes in if blocks, for = example) and other such dynamic behavior. Even if we could > fix this issue, there is still an issue if A, B and C were defined in = separate files and then included. You couldn't require them > in any order for it to work; you would have to autoload. >=20 > Option 2: Covariant return types with runtime checking > ------------------------------------------------------ > This option would do the variance check when the method is used (or = potentially when the class is instantiated, or > whichever comes first). > Regardless of the exact details of how this method is implemented, the = above code would work because the first time class > B is used in any way occurs after C is defined. This would also work = if you separated them out into different files >=20 > This option would be slower than option 1, but cannot quantify it = because it is not yet implemented. I suspect there would > be a way to cache the result of the variance check to not have to do = it on every instantiation or invocation, so this may be > negligble. >=20 > This option has the drawback that inheritance problems can exist in = the code and won't be discovered until the code is ran. > Let me repeat that to make sure that everyone understand it: if you = have return type variance errors in your code, they > would not be detected until you try to use the class or method. >=20 > Option 3: Invariant return types with definition time checking > -------------------------------------------------------------- > This means that the declared types must *exactly match* after = resolving aliases such as parent and self. The advantage of > this option is that the inheritance check can be done at definition = time in all cases without triggering an autoload. As such it > is a bit simpler to implement and is slightly faster to do so. The = obvious downside is that it can't support covariant return > types. >=20 > --- >=20 > Note that C++ and Java (and most modern languages) support covariant = return types, but C# supports only invariant return > types. >=20 > Which of these methods do you prefer the most, and which do you prefer = the least, and why? >=20 > -- It should be mentioned for Option 3 that covariant return types are not = supported just at the definition level . Even with invariant return = types the following is perfectly fine: class A { function foo(): B { return new B; } } class B extends A { function foo(): B { return new C; } } Needless to say, IDEs using the type hints will not be able to support = the user properly with such a construct (without additional type = annotation in PHPDoc or such and then the annotation in the code becomes = useless) I am not sure what suits best for PHP but right now, I would say = invariant for the following three reasons: 1. Parameters are invariant as well -> same behaviour 2. Invariant can be extended to covariant support later on without BC = break, the other way round is not possible 3. The simplicity hopefully facilitates that this RFC will be accepted As a side notice, C# allows to define covariant return types by using = generics and corresponding constraints.