On Sat, Aug 14, 2021 at 1:27 AM Jordan LeDoux <jordan.led...@gmail.com>
wrote:

> Hey internals,
>
> I've been working on the draft for my operator overloading RFC, and in
> doing so I encountered a separate change that I would like to see.
>
> That is, the use of `never` as an argument type for interfaces. Since
> arguments in PHP are contravariant to preserve Liskov substitution, `never`
> as the bottom type should indicate that implementing classes can require
> any type combination they want. This is in fact consistent with type theory
> and set theory, and is how the bottom type is treated in several other
> languages.
>
> In this case, the bottom type would be used to indicate covariant parameter
> polymorphism while not conflicting with LSP.
>
> This would provide a sort of minimal form of generics to PHP without the
> issues that actual generics present from an implementation perspective. It
> would not, however, restrict or hinder any future RFC for generics.
>
> This is at the first draft stage, and I currently have the RFC on a github
> repo to allow for easy contribution and collaboration.
>
> Any feedback is greatly appreciated.
>
> https://github.com/JordanRL/never-argument-type
>

There's two sides to this coin: While using a never argument type allows
you to avoid the LSP problem, it also means that you cannot call the method
while typing against the interface. Let's take your CollectionInterface

interface CollectionInterface {
    public function add(never $input): self;
}

and actually try to make use of it:

function addMultiple(CollectionInterface $collection, mixed ...$inputs):
void {
    foreach ($inputs as $input) $collection->add($input);
}

A static analyzer should flag this CollectionInterface::add() call as
invalid, because mixed is passed to never. Effectively, this means that an
interface using never argument types cannot actually be used in anything
*but* inheritance -- so what is its purpose?

Compare this to generics. The interface changes to

interface CollectionInterface<T> {
    public function add(T $input): self;
}

and the use changes to

function addMultiple<T>(CollectionInterface<T> $collection, T ...$inputs):
void {
    foreach ($inputs as $input) $collection->add($input);
}

Now a T argument is passed to a T parameter, and everything is fine.

Regards,
Nikita

Reply via email to