On Monday, 2 June 2025 at 21:53, Rowan Tommins [IMSoP] <imsop....@rwec.co.uk> wrote:
> On 02/06/2025 17:27, Gina P. Banyard wrote: > >> The objective is to fix a weird quirk of PHP's type system, where void lives >> in its own type hierarchy. >> This is visible mainly in that a lack of return type is not isomorphic to a >> function that has a return type of mixed. > > I think if "void" was added now, it would be an attribute, rather than a > type. It is in effect the exact opposite of #[\NoDiscard], and distinguishes > between these two cases: > > interface Foo { > public function getSomething(): ?Something; > } > > class MyFoo implements Foo { > public function getSomething(): null { > // My result is always null, but still meaningful to consumers of the Foo > interface > return null; > } > > #[\DiscardReturn] > public function doSomething(): null { > // I have no meaningful information to return; any assignment of my implicit > value is a mistake > } > > } A function that always returns the same value is not meaningful to a consumer, the only exception is in a class hierarchy with an overloaded method. But in that case the consumer expects potential other concrete values, so this point is a bit moot IMHO. In the same way as saving the result of print() is pointless because it always returns 1, or a function that always returns true. > I agree the type hierarchy you describe is weird, but rather than throwing > away the functionality completely, I wonder if we can make it more consistent: > > - Make "no return type declared" and "mixed" equivalent > - Make "void" a sub-type of "null", and therefore a sub-type of "mixed" > > If I've got that right, this would then be legal: > > class > > A > > { > > public > > function > > foo > > ( > > ) > > { > > } > > } > > class > > B extends A > > { > > public > > function > > foo > > ( > > ) > > : mixed > > { > > } > > } > > class > > C extends B > > { > > public > > function > > foo > > ( > > ) > > : null > > { > > } > > } > > class > > D extends C > > { > > public > > function > > foo > > ( > > ) > > : void > > { > > } > > } > > class > > E extends D > > { > > public > > function > > foo > > ( > > ): never > > { > > } > > } > > That seems reasonable enough; I may have missed something important, though Not sure if it is important, but you are missing the case where null takes part in a union type. Your proposed change would also allow the following class hierarchy: class A { public function foo ( ) { } } class B extends A { public function foo ( ) : mixed { } } class C extends B { public function foo ( ) : string|int|null { } } class D extends C { public function foo ( ) : void { } } class E extends D { public function foo ( ): never { } } But if people think this type hierarchy makes more sense, then sure. I am not convinced, as I think it is a *good* thing PHP always returns a value from a function. Lying about this just seems pointless and leading to misunderstandings about how the language behaves. Best regards, Gina P. Banyard