Andrey Andreev <n...@devilix.net> wrote:

> When I said I don't claim to fully understand LSP

Hi Andrey,

The RFC specifically didn't mention LSP....because that is separate
from co/contravariance. It's unfortunate for other people to be
throwing the two around at you with a lack of precision.

(disclaimer - I helped Niklas draft the RFC, but I still muddle up the
terms occassionaly also.)

> What I don't understand is how do we get from this...To this:

It's not difficult, just subtle. First, some code, to define some classes:

interface A {
    function foo(Bar $bar);
}

class B implements A {
    function foo($bar) {
        // this function is capable of handling either objects
        // of type Bar, or strings
    }
}

There are two separate things here.

First - method B::foo is contravariant to method A::foo, which just
means that anything that can be successfully passed as the parameter
to A::foo can also be passed to B::foo.

Second - because of the contravariance, and because PHP allows classes
to implement interfaces, the Liskov substition principle kicks in, and
we can substitute an object of type B anywhere where an object of type
A is expected.

e.g. If we have a function that takes A as a parameter:

function doSomething(A $a) {
    $a->foo(new Bar());
}


Both of these are guaranteed* to be work equivalently due to LSP:

doSomething(new A());
doSomething(new B());


> by the above logic, that it considers "mixed" a subtype of ... everything?

No, I think you've been confused by people saying LSP where they meant
contravariance. Although class B can accept either an object of type
Bar, or a string that doesn't meant that a string is a sub-type of
Bar.

Instead because B obeys the contravariance rules for the
implementation of the method, an object of type B can be substituted
where A is expected.

Incidentally, this would be a lot nicer if we had passed the union
type RFC, and so were able to write:

class B implements A {
    function foo(Bar|string $bar) {
         // hurrah for static code inspection.
    }
}


Or for avoiding 'clashing' interfaces:

interface C {
    function foo(Bar $bar);
}
interface D {
    function foo(string $bar);
}
class E implements C, D {
    function foo(Bar|string $bar) {
        //This method satifies the contracts for both interfaces.
    }
}

> Every (re)definition and example talks about
> substituting *objects* with their subtypes

It is a little unfortunate that a lot of information about type
systems is presented in object-oriented languages, as the same thing
applies to things that aren't objects, e.g. arrays, generators =>
iterable and also callback functions e.g. the callable passed to
set_exception_handler() must accept at least \Exception in PHP 5.6 and
\Throwable in PHP 7, but it's fine for it to accept a wider set of
parameters.

cheers
Dan

*guarantees about program behaviour with LSP are kind of...bogus. In
fact it's probably better to concentrate on how it usually makes your
program be easier to write, rather than relying too heavily on it for
an application to actually 'behave' correctly in all scenarios.

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

Reply via email to