Re: [RFC] Never For Argument Types
| From: | Jordan LeDoux | Date: | Sun, 15 Aug 2021 11:11:16 +0000 |
| Subject: | Re: [RFC] Never For Argument Types | ||
| References: | 1 2 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-115738@lists.php.net to get a copy of this message | ||
On Sun, Aug 15, 2021 at 3:43 AM Lynn <kjarli@gmail.com> wrote:
> On Sat, Aug 14, 2021 at 1:27 AM Jordan LeDoux <jordan.ledoux@gmail.com>
> wrote:
>
>>
>> Any feedback is greatly appreciated.
>>
>
> https://github.com/JordanRL/never-argument-type
>
>
> My only feedback is that it should not be called
never. If I
> see never
> as the parameter type, I read this as never being allowed to pass anything,
> which means that foo('something') with
> foo(never $something) will
> result in a compile error. The type never only makes sense from
> a return
> type perspective: "Should it return? never". Perhaps
> any or anything
> makes more sense? I've also seen a suggestion to name it
> abstract, and
> maybe there are more suggestions I've missed. To me it also sounds like the
> goal of never as a parameter type is different from the return
> type. From
> my understanding one indicates that a type is required, while the other
> indicates that nothing ever returns, giving it the same name is confusing.
>
>
> Apologies for sending this message twice to you Jordan, reply to all is
> not my default reply button.
>
No worries :)
So your intuition is correct. foo(never $something) will result in a
TypeError if you try to call it directly. The only way it can be called
is by being overridden with a wider type, which is why most of the focus is
around inheritance. Technically, you *could* use it in a function and that
would make the function uncallable, because any function with a parameter
of type never is uncallable unless it is overridden and widened.
This is in line with type theory for how bottom types work in other
languages as well.
My biggest worry about using something besides never is that it *is* the
bottom type already. It's only usable in return types, but within the
engine it *is* the bottom type. That's what the bottom type in a type
system generally means: this can't be used and/or the program stops if this
is used. If we made a bottom type for returns (which we have), and then
made a *different* bottom type for arguments, we'd be in a situation that
is unique in programming languages as far as I can tell. I have done a bit
of research into this as part of the RFC, and I haven't found a single
language with multiple bottom types. :/
TypeScript, Elm, and PHP have the bottom type never. Ceylon, Kotlin, and
Scala have the bottom type Nothing. Rust has the bottom type !,
JavaScript with Closure Compiler annotations has !Null (which is intended
to mean a non-null member of the null type), Julia has Union{}, Python
has typing.NoReturn, and Lisp has NIL.
None of them (so far) have multiple bottom types for different
circumstances.
And the meaning of the type isn't *exactly* "any". It's more like
"none".
But the type string contains the "none" concept also. You take
"none" and
then you add the parts that make a string type to "none", and what you're
left with is just the string type. That's why it can be broadened to
anything, because *all* types contain never, just like you can subtract 0
from any integer.
I have been focusing on the use cases, which are about how this interacts
with widening and inheritance. But the type itself I would argue is fully
descriptive where it is defined: that code can never be called due to the
type on the parameter being never. It must be inherited. If we did
something like any, that would indicate that the intent for inheritance
more clearly, but think about the code it's actually defined in. If you had
the following code:
```
class A {
public function doSomething(any $var): string {
}
}
```
Would you expect calling that method to result in a TypeError? Because
that's what *should* happen. The code it is actually written in cannot
accept any type, in fact it can never accept a type.
Jordan