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

Reply via email to