Skip to content

Instantly share code, notes, and snippets.

@nikita2206
Last active August 29, 2015 14:17
Show Gist options
  • Save nikita2206/921ea24e935e5b6d1c6c to your computer and use it in GitHub Desktop.
Save nikita2206/921ea24e935e5b6d1c6c to your computer and use it in GitHub Desktop.

A -> B (A parent of B); foo(callable(B) $cb); In this case you can actually pass a callable that takes a superset of what is required: foo(function (A $a){}); - that will work without any problems. But when we go one level deeper: foo(callable(callable(B)) $cb); When you will pass a lambda to foo(), it will have to expect another lambda. And this last lambda signature should not relax requirements on the input because otherwise this will not be possible:

foo(callable(callable(B)) $cb) { $cb(function (B $b) {  }); }

If you try to call foo() like this:

// Tried to relax requirements, going from B to A
foo(function (callable(A) $cb) {
    $cb(new A);
});

It is not compatible now, because in the body of the foo() we pass callable(B) to $cb, but $cb expects callable(A) (a superset). But we could perfectly go backwards:

foo(callable(callable(A)) $cb) { $cb(function (A $a) {}); }

foo(function (callable(B) $cb) {
    $cb(new B);
});

Because B is a subset of A, we can pass it to the function that requires A, so this case is valid.

Let's go one level deeper with respectively the same types (in foo() definition we leave A and when we call it we only use B):

foo(callable(callable(callable(A))) $cb) {
    $cb(function (callable(A) $cb) {
        $cb(new A);
    });
}

foo(function (callable(callable(B)) $cb) {
    $cb(function (B $b) { });
});

Now it broke, because inside of foo() when we call $cb(new A); we will send a superset of what function (B $b) {} expects. If you mentally replace all B's with A's and A's with B's you will see that this way it will work - hence variance was inverted. Let's go just one level more...

foo(callable(callable(callable(callable(A)))) $cb) {                      
    $cb(function cb1(callable(callable(A)) $cb) {                   /-\ cb3()
        $cb(function cb2(A $cb) {                                   | |     /-\ cb4()
                                                                    | |    |   \      /
        });                                                         | |    |    |     |
    });                                                             | |    |    |     |
}                                                                   | |    |    |     |
                                              // first we call foo -/ |   /     |     |
foo(function cb3(callable(callable(callable(B))) $cb) {               |  /      |    /
    $cb(function cb4(callable(B) $cb) {                               \_/ cb1() |   /
        $cb(new B);                                                              \_/ cb2()
    });
});

This time it works, in the end we send B to the callback that expects A (a supertype of B)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment