> -----Ursprüngliche Nachricht-----
> Von: morrison.l...@gmail.com [mailto:morrison.l...@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
> 
> Dear Internals,
> 
> 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.
> 
> The following code demonstrates the design flaw mentioned above:
> 
>     class A {
>         function foo(): B { return new B; }
>     }
> 
>     class B extends A {
>         function foo(): C { return new C; }
>     }
> 
>     class C extends B {}
> 
>     $b = new B;
>     $c = $b->foo();
> 
> I've also used it because it can adequately show the differences in how each 
> of the following options work:
> 
>   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
> 
> 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.
> 
> 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.
> 
> 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
> 
> 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.
> 
> 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.
> 
> 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.
> 
> ---
> 
> Note that C++ and Java (and most modern languages) support covariant return 
> types, but C# supports only invariant return
> types.
> 
> Which of these methods do you prefer the most, and which do you prefer the 
> least, and why?
> 
> --

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.


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to