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