Robert Stoll wrote on 05/02/2015 12:16:
-----Ursprüngliche Nachricht-----
Von: julienpa...@gmail.com [mailto:julienpa...@gmail.com] Im Auftrag von Julien 
Pauli
Gesendet: Donnerstag, 5. Februar 2015 13:10
An: Andrea Faulds
Cc: Hannes Magnusson; Nikita Popov; PHP internals
Betreff: Re: [PHP-DEV] Allow dropping typehints during inheritance

On Thu, Feb 5, 2015 at 2:08 AM, Andrea Faulds <a...@ajf.me> wrote:

Hi Hannes,

On 4 Feb 2015, at 23:58, Hannes Magnusson
<hannes.magnus...@gmail.com>
wrote:
So what it supports "more inputs"?
It does constitute an LSP violation. "more inputs" is not what the
guarantee is at all, if that is what you want you'd typehint on a
interface.


It is a LSP failure to allow a string, or any other scalar value,
when the parent requires a specific type/object.

It sucks that we fail our arginfo very frequently, but this is the
way
it is :]

An interface requires only a minimum standard for accepted input. If a
class implementing that interface allows a wider range of values, but
still allows at least what the interface requires it to, it is not an
LSP violation. Scalars are not special.

If we allow larger type, why doesn't such code work ?

interface A { }
interface B extends A { }

class C {
     public function foo(A $a) { }
}

class D extends C {
     public function foo(B $a) { } // E_STRICT }

This is wrong IMO.

Because this would be covariance and violates LSP. Considering the following:

function foo(C $c){
    $c->foo(new A);
}

if you substitute C with D then suddenly the code would not work anymore.

Indeed. The contract states that if you know you have an instance of C, you can pass any instance of A to its foo method. If you are given an instance of D, that contract will be violated, because instances of A which are not instances of B will be rejected.


The other way around, however, is theoretically fine (though not implemented in PHP because its complicated to check):

class X { public function bar(B $thing) {} }
class Y extends X { public function bar(A $thing) {} }

Here, the contract states that if you have an instance of X, you can pass any instance of B to its bar method. If you are given an instance of Y, you can still pass any B to its bar method, so the contract is met. If you /know/ that you have an instance of Y, you know you can also pass other types of A//.


A real-life example that came to mind previously was an event handler interface that told implementers exactly what kind of event to expect, being implemented by a logger that can actually accept any kind of event you give it. As long as the event dispatchers knows that the logger accepts the event its dispatching, it doesn't care what else it can do.

Regards,
--
Rowan Collins
[IMSoP]

Reply via email to