Skip to content
On this page

Generics: Variance

Ara supports covariance for generics.

Covariant type parameters are specified using the + prefix. For example, the following type is generic over a covariant type parameter +T:

readonly class Box<+T> {
    public function __construct(public T $value) {}

    public function get(): T {
        return $this->value;
    }
}

interface Animal {}

final class Cat implements Animal {}
final class Dog implements Animal {}

function pet(Box<Animal> $animal): void {
    IO\write_line("Petting %s", $animal->value::class);
}

function pet(vec<Animal> $animals): void {
    foreach $animals as $animal {
        IO\write_line("Petting %s", $animal::class);
    }
}

function example(): void {
    pet(new Box::<Cat>(new Cat()));
    pet(new Box::<Dog>(new Dog()));

    pet_all(vec[new Cat(), new Cat()]);
}

If a type parameter is covariant, then it can be used as the type of a parameter that expects a subtype of the type parameter. For example, the pet() function can be called with a Box<Cat> or a Box<Dog> because Box<Cat> and Box<Dog> are subtypes of Box<Animal>.

If we have declared Box as invariant, then the pet() function would not be able to be called with a Box<Cat> or a Box<Dog> because Box<Cat> and Box<Dog> are not subtypes of Box<Animal>.

Licensed under either of the MIT License or the Apache License (Version 2.0), at your option.