Lazare Inepologlou wrote on 26/11/2014 10:21:
* Parameter types are contravariant, otherwise they are not type sound.
Yet, contravariance in general is of little interest (I cannot think of any
practical example), so invariance is a good compromise.
After reading the Wikipedia article, I've been thinking of some
practical example of contravariance in PHP. One involves the Iterator
and Traversable interfaces: imagine we have this class:
class Foo { public function iterate(Iterator $i) { /*...*/ } }
Now we make a sub-class which implements its iterate method using a
foreach() loop, so can transparently accept any Traversable:
class Bar extends Foo { public function iterate(Traversable $t) {
/*...*/ } }
This requires contravariance, because the child class accepts everything
the parent class would, but doesn't have an identical type hint because
it also accepts more.
A more involved example would be this:
// Two types of user, both extending a base class
abstract class User { abstract function getDisplayName(); /*...*/ }
class VisitingUser extends User { function getDisplayName() { return
'Anonymous Coward'; } /*...*/ }
class RegisteredUser extends User { function getDisplayName() { /*...*/
} function getUserID { /*...*/ } /*...*/ }
// An interface for injecting observers of social events carried out by
registered users
interface SocialEventListener { function handleUserEvent( RegisteredUser
$u, Event $e ); }
// An implementing class can safely use methods only present for
registered users without additional type checks
class UserTimelineWriter implements SocialEventListener { function
handleUserEvent( RegisteredUser $u, Event $e ) { $user_id =
$u->getUserID(); /*...*/ } }
// However, a more general observer might not use the user ID, and so
could be reused for events with any kind of user
interface UserActionListener { function handleUserEvent( User $u, Event
$e ); }
class EventLogger implements UserActionListener, SocialEventListener{
function handleUserEvent( User $u, Event $e ) { /* code relying only on
$u->getDisplayName() ... */ } }
Without parameter contravariance, there is no way to achieve this. Since
PHP doesn't have method overloading, you can't add a second version of
handleUserEvent which accepts only RegisteredUser arguments, so you have
to either change the name of the method in one of the interfaces, or
create an entire adapter class just to change the type hint.
Obviously, the exact details here are contrived to make the point, but
they don't seem all that far-fetched to me.
Regards,
--
Rowan Collins
[IMSoP]
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php