On Sat, Aug 14, 2021 at 1:27 AM Jordan LeDoux <[email protected]>
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