Marc,

On Wed, Apr 1, 2015 at 2:46 PM, Marc Bennewitz <dev@mabe.berlin> wrote:
> Hi internals,
>
> On experimenting with return type-hints I noted an inconsistency on
> overwriting an existing hint to a more specific one. On adding a non
> existing return type-hint on overwrite it's fine but it's logically the same
> (this previously was an E_STRICT).
>
> http://3v4l.org/YdB8s - works fine (E_STRICT lower then php7@20150401)
> http://3v4l.org/UDk0n - Fatal error: Declaration of Bar::setTest() must be
> compatible with Foo::setTest($test): Foo
> http://3v4l.org/AuOsr - HHVM works fine

That's the concept of Variance:
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)

Specifically, that return types can be covariant (subtypes can
"narrow" return types) and parameters can be contravariant (subtypes
can "widen" paramters). So this is valid:

class A extends B {}

class C {
    public function foo(A $b): B {}
}

class D extends C {
    public function foo(B $b): A {}
}

In that everything that C::foo accepts, D::foo accepts, but D::foo can
accept more.

And D produces more strictly (in that anything that can work with a
product of C::foo can work with what D::foo produces).

We looked into doing this for return types (and all other
declarations). However, we ran into some non-trivial problems around
compilation.

Currently, types are never loaded from a declaration. They are only
loaded when instantiated. Therefore, it's completely possible to have
a declared class with undeclared type declarations. If the type
declaration remains undeclared when you call the method, it's by
definition a failed type (since `$obj instanceof UndefinedClass` is
always false).

If we want to check variance, we need to know about the type at
compile time. This brings up an issue that basically makes it
impossible to separate code into multiple files without an autoloader.
For example:

<?php // a.php
class A {
    public function foo(B $bar) {}
}

<?php // b.php
class B {
    public function bar(A $foo) {}
}

You couldn't have that be possible without an autoloader, since
there's a circular dependency. Sure, we could (and should) special
case the "doesn't inherit" situation, but it's pretty trivial to make
a new example which requires an autoloader.

So that leaves us with a weird situation: to support the feature (in
general), you'd have to use an autoloader or put everything into a
single file. That may be OK, but it's also added complexity.

To the best of my knowledge, Levi removed variance from the proposal
to keep it simple (just like nullables weren't in the proposal):
https://wiki.php.net/rfc/return_types#variance_and_signature_validation

If you'd like to create a proposal for 7.1 to add variance support to
type declarations, I think that would be awesome. It's not terribly
difficult, but there are some gotchas (especially around opcache
support) and non-trivial tradeoffs involved.

Anthony

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

Reply via email to