Hi Tim and Larry, Thanks for sharing examples. I'm not sure I follow how never parameters help in either of these cases. As far as I can tell, the problem remains that these methods can't actually be called.
On Fri, Mar 21, 2025 at 4:50 AM Tim Düsterhus <t...@bastelstu.be> wrote: > Am 2025-03-20 21:27, schrieb Matt Fonda: > > If an interface adds a method but makes no promises about what > > parameters > > it accepts, then why is it part of the interface in the first > > place--why > > add a method that can't be used? > > It would more cleanly allow for userland / PHPDoc-based generics, while > still providing some engine-enforced type safety. Consider this example > (not sure if I got the syntax completely right): > > /** @template T */ > interface Comparable { > /** @param T $other */ > public function compareTo(never $other): int; > } > > /** @implements Comparable<Number> */ > final class Number implements Comparable { > public function compareTo(Number $other): int { return $this <=> > $other; } > } I don't follow why Number would implement Comparable in the first place, since we won't actually be able to call Comparable::compareTo. i.e. we cannot write the following method: function greaterThan(Comparable $a, Comparable $b): bool { return $a->compareTo($b) > 0; } If we wanted to actually call compareTo, we need to use a specific implementation: function greaterThan(Number $a, Number $b): bool { return $a->compareTo($b) > 0; } At this point, we're no longer using the interface, so there's no point in Number implementing it. Number is then free to define compareTo(Number $other). I share Nikita's sentiment in the previous RFC discussion [1], and have yet to see an answer to it: I don't think this really addresses my concern, so let me repeat it: You > cannot actually call a method using a never-type argument while typing > against the interface. What's the point of the interface then? > > I don't think "you must use this in conjunction with a 3rd-party phpdoc > generics implementation for it to make any sense at all" is a suitable way > to resolve that. On Fri, Mar 21, 2025 at 8:53 AM Larry Garfield <la...@garfieldtech.com> wrote: > Changing the interface to use `never` instead of `mixed` would have the > weakest guarantees of the three, since it doesn't force me to use the > *same* widened type on serializeInt(), serializeFloat(), serializeString(), > etc., even though it would always be the same. But it would allow me to > communicate more type information than I can now. > Suppose you made this change from mixed to never. As long as you're typing against the Formatter interface and not a specific implementation, then you cannot actually call $formatter->serializeInt() etc. because the interface defines the parameter type as never, and you cannot call a method with a parameter type of never. This is the case in your real world usage [1]. Here, $serializer->formatter is typed against Formatter [2], not a specific implementation. As such, all we know is that we have an instance of Formatter--we don't know anything about the concrete type--and thus we can't actually call e.g. serializeInt() because never is the only type we know here. Best regards, --Matthew [1] https://externals.io/message/115712#115752 [2] https://github.com/Crell/Serde/blob/777fc16e932d4dcf1d600335961685885cd815c4/src/PropertyHandler/ScalarExporter.php#L17 [3] https://github.com/Crell/Serde/blob/777fc16e932d4dcf1d600335961685885cd815c4/src/Serializer.php#L31