Generics in
#posobota @jiripudil
Jiří Pudil
from Brno, CZE
open-sourcerer
coffee addict
guitar noisemaker
What are generics<?>
You might have already used them.
What are generics?
/**� * @var Animal[]� */�private array $pets = [];
What are generics?
/**� * @var array<Animal>� */�private array $pets = [];
What are generics?
/**� * @var array<int|string, Animal>� */�private array $pets = [];
What are generics?
/**� * @var array<int, Animal>� */�private array $pets = [];
What are generics?
/**� * @var list<Animal>� */�private array $pets = [];
Writing List by hand
Writing List by hand
class List {� public function get($index) {}� public function add($value) {}�}
Writing List by hand
class List {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}
Writing List by hand
class List {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}
class ListOfIntegers extends List {� public function get(int $index): int|null {}� public function add(int $value): void {}�}
Writing List by hand
class List {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}
class ListOfStrings extends List {� public function get(int $index): string|null {}� public function add(string $value): void {}�}
Writing List by hand
class List {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}
class ListOfAnimals extends List {� public function get(int $index): Animal|null {}� public function add(Animal $value): void {}�}
A generic List
Writing List by hand
class List {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}
Writing List by hand
class List<ValueType> {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}
Writing List by hand
class List<ValueType> {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}
$listOfIntegers = new List<int>();�$listOfStrings = new List<string>();�$listOfAnimals = new List<Animal>();
Writing List by hand
class List<ValueType> {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}
$listOfIntegers = new List<int>();�$listOfIntegers->add(42);�$answer: int = $listOfIntegers->get(0);
Writing List by hand
class List<ValueType> {� public function get(int $index): ValueType|null {}� public function add(ValueType $value): void {}�}
$listOfIntegers = new List<int>();�$listOfIntegers->add("42");
Generics are hard
Generics in
Not anytime soon*
Generics in
List
/**� * @template ValueType� */�class List�{� /** @return ValueType|null */� public function get(int $index): mixed;�� /** @param ValueType $value */� public function add(mixed $value): void;�}
List
/** @var List<int> */�$listOfIntegers = new List();
$listOfIntegers->add(42);�$answer = $listOfIntegers->get(0);�assert(is_int($answer));
$listOfIntegers->add("42");
List
/**� * @param List<int> $list� */�function count(List $list): int { /* … */ }
List
/**� * @return List<int>� */�function getDiceRolls(int $howMany): List { /* … */ }
Type inference
Type inference
/**� * @template T� * @param List<T>� * @return List<T>� */�function sort(List $list): List { /* … */ }
Type inference
$sortedList = sort($listOfIntegers);�// $sortedList is List<int>
Type bounds
Type bounds
/**� * @template ValueType of Animal� */�class List�{�}
Type bounds
/** @var List<Dog> */�$list = new List();
Type bounds
/** @var List<Car> */�$list = new List();
Type bounds
/**� * @template T of Comparable� * @param List<T>� * @return List<T>� */�function sort(List $list): List { /* … */ }
Type bounds
/**� * @template E of Entity� */�class Repository�{� /** @return E */� function get(int $id): Entity { /* … */ }�}
Variance
Variance
Animal
Dog
Variance
Animal
Shelter<Animal>
Dog
Shelter<Dog>
?
Variance
Animal
AnimalShelter
Dog
DogShelter
?
Variance
Animal
AnimalShelter
Dog
DogShelter
Variance
class AnimalShelter�{� function getAnimalForAdoption(): Animal { /* … */ }� function putAnimal(Animal $animal): void { /* … */ }�}
Variance
class AnimalShelter�{� function getAnimalForAdoption(): Animal { /* … */ }� function putAnimal(Animal $animal): void { /* … */ }�}
class DogShelter extends AnimalShelter�{� function getAnimalForAdoption(): Animal { /* … */ }� function putAnimal(Animal $animal): void { /* … */ }�}
Variance
class AnimalShelter�{� function getAnimalForAdoption(): Animal { /* … */ }� function putAnimal(Animal $animal): void { /* … */ }�}
class DogShelter extends AnimalShelter�{� function getAnimalForAdoption(): Dog { /* … */ }� function putAnimal(Animal $animal): void { /* … */ }�}
Variance
class AnimalShelter�{� function getAnimalForAdoption(): Animal { /* … */ }� function putAnimal(Animal $animal): void { /* … */ }�}
class DogShelter extends AnimalShelter�{� function getAnimalForAdoption(): Dog { /* … */ }� function putAnimal(object $animal): void { /* … */ }�}
Generic type variance
Generic type variance
Animal
Shelter<Animal>
Dog
Shelter<Dog>
?
Generic type variance
Animal
Shelter<Animal>
Dog
Shelter<Dog>
X
Generic type covariance
Generic type covariance
Animal
Shelter<Animal>
Dog
Shelter<Dog>
Generic type covariance
/** @param Shelter<Animal> $shelter */�function listAllAnimalsFrom(Shelter $shelter): array { /* … */ }��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��listAllAnimalsFrom($dogShelter);
Generic type covariance
/** @param Shelter<Animal> $shelter */�function listAllAnimalsFrom(Shelter $shelter): array { /* … */ }��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��listAllAnimalsFrom($dogShelter);
Generic type covariance
/**� * @template-covariant A of Animal� */�class Shelter�{� /** @return A */� function getAnimalForAdoption(): Animal { /* … */ }�� /** @param A $animal */� function putAnimal(Animal $animal): void { /* … */ }�}
Generic type covariance
/** @param Shelter<Animal> $shelter */�function listAllAnimalsFrom(Shelter $shelter): array { /* … */ }��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��listAllAnimalsFrom($dogShelter);
Generic type covariance restrictions
Generic type covariance restrictions
/** @param Shelter<Animal> $shelter */�function addAnimalsTo(Shelter $shelter): void {� $cat = new Cat();� $shelter->add($cat);�}��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��addAnimalsTo($dogShelter);
Generic type covariance restrictions
/**� * @template-covariant A of Animal� */�class Shelter�{� /** @return A */� function getAnimalForAdoption(): Animal { /* … */ }�� /** @param A $animal */� function putAnimal(Animal $animal): void { /* … */ }�}
Generic type covariance restrictions
/** @param Shelter<Animal> */�function addAnimalsTo(Shelter $shelter): void {� $cat = new Cat();� $shelter->add($cat);�}��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��addAnimalsTo($dogShelter);
Generic type covariance restrictions
/**� * @template-covariant A of Animal� */�class Shelter�{� /** @return A */� function getAnimalForAdoption(): Animal { /* … */ }�� /** @param A $animal */� function putAnimal(Animal $animal): void { /* … */ }�}
Generic type covariance restrictions
/**� * @template-covariant A of Animal� */�class Shelter�{� /** @return A */� function getAnimalForAdoption(): Animal { /* … */ }�� /* nada, this class must be readonly */�}
Type projections
Type projections
/**� * @template A of Animal� */�class Shelter�{� /** @return A */� function getAnimalForAdoption(): Animal { /* … */ }�� /** @param A $animal */� function putAnimal(Animal $animal): void { /* … */ }�}
Type projections
/** @param Shelter<covariant Animal> $shelter */�function listAllAnimalsFrom(Shelter $shelter): array { /* … */ }��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��listAllAnimalsFrom($dogShelter);
Type projections
/** @param Shelter<covariant Animal> $shelter */�function addAnimalsTo(Shelter $shelter): void {� $cat = new Cat();� $shelter->add($cat);�}��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��addAnimalsTo($dogShelter);
Type projections
/** @param Shelter<covariant Animal> $shelter */�function addAnimalsTo(Shelter $shelter): void {� $cat = new Cat();� $shelter->add($cat);�}��/** @var Shelter<Dog> */�$dogShelter = new Shelter();��addAnimalsTo($dogShelter);
Soon* in
Generic type contravariance
Generic type contravariance
Animal
Shelter<Animal>
Dog
Shelter<Dog>
Generic type contravariance
Shape
Comparator<Shape>
Square
Comparator<Square>
Generic type contravariance
/**� * @template T� */�class List�{� /** @param Comparator<T> $comparator */� function sort(Comparator $comparator): self { /* … */ }�}
Generic type contravariance
/**� * @template T� */�interface Comparator�{� /**� * @param T $a� * @param T $b� */� public function compare($a, $b): int;�}
Generic type contravariance
interface Shape {� public function area(): int;�}��class Square implements Shape {}��/** @implements Comparator<Shape> */�class ShapeByAreaComparator implements Comparator�{�}
Generic type contravariance
interface Shape {}�class Square implements Shape {}��/** @implements Comparator<Shape> */�class ShapeByAreaComparator implements Comparator�{�}��/** @var List<Square> $listOfSquares */�$listOfSquares->sort( /* expects Comparator<Square> */ );
Generic type contravariance
interface Shape {}�class Square implements Shape {}��/** @implements Comparator<Shape> */�class ShapeByAreaComparator implements Comparator�{�}��/** @var List<Square> $listOfSquares */�$listOfSquares->sort(new ShapeByAreaComparator);
Generic type contravariance
/**� * @template-contravariant T� */�interface Comparator�{� /**� * @param T $a� * @param T $b� */� public function compare($a, $b): int;�}
Generic type contravariance restrictions
Generic type contravariance
/**� * @template-contravariant T� */�class List�{� /**� * @return T� */� public function get(): mixed;�}
Soon* in
Contravariant type projections
Contravariant type projections
/**� * @param List<contravariant Square> $list� */�function checkAll(List $list): void { /* … */ }
Contravariant type projections
/**� * @param List<contravariant Square> $list� */�function checkAll(List $list): void {� /** @var mixed $square */� $square = $list->get();�}
Contravariant type projections
/**� * @template T of Shape� */�class List�{� /**� * @return T� */� public function get(): mixed;�}
Contravariant type projections
/**� * @param List<contravariant Square> $list� */�function checkAll(List $list): void {� /** @var Shape $square */� $square = $list->get();�}
Generic star projection*
Generic star projection
/**� * @param List<covariant mixed> $list� */�function checkAll(List $list): void {� /** @var mixed $item */� $item = $list->get();�� $list->add(42);�}
Generic star projection
/**� * @param List<contravariant never> $list� */�function checkAll(List $list): void {� /** @var mixed $item */� $item = $list->get();�� $list->add(42);�}
Generic star projection
/**� * @param List<*> $list� */�function checkAll(List $list): void {� /** @var mixed $item */� $item = $list->get();�� $list->add(42);�}
Soon* in
What are generics<?>
Any other questions<?>