Re: [RFC] [Discussion] Never parameters

From: Date: Fri, 28 Mar 2025 20:42:46 +0000
Subject: Re: [RFC] [Discussion] Never parameters
References: 1 2 3 4 5  Groups: php.internals 
Request: Send a blank email to [email protected] to get a copy of this message
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 <[email protected]> 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 <[email protected]>
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


Thread (33 messages)

« previous php.internals (#126971) next »