Re: Proposal: AS assertions
| From: | Rowan Tommins [IMSoP] | Date: | Thu, 21 Mar 2024 20:36:24 +0000 |
| Subject: | Re: Proposal: AS assertions | ||
| References: | 1 2 3 4 5 6 7 8 9 10 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-122718@lists.php.net to get a copy of this message | ||
On 21/03/2024 19:03, Robert Landers wrote:
I suppose we are taking this from different viewpoints, yours appears to be more of a philosophical one, whereas mine is more of a practical one.My main concern is consistency; which is partly philosophical, but does have practical impact - the same syntax meaning the same thing in different contexts leads to less user confusion and fewer bugs. But I also think there are real use cases for "error on anything other than either Foo or null" separate from "give me a null for anything other than Foo".
$x = $a as null; (or any other value, such as true|false) appears to have no practical purpose in this particular case.There's plenty of possible pieces of code that have no practical purpose, but that on its own isn't a good reason to make them do something different. "null" as a standalone type (rather than part of a union) is pretty much always pointless, and was forbidden until PHP 8.2. It's now allowed, partly because there are scenarios involving inheritance where it does actually make sense (e.g. narrowing a return type from Foo|null to null); and probably also because it's easier to allow it than forbid it. That's not really what we're talking about anyway, though; we're talking about nullable types, or null in a union type, which are much more frequently used.
Further, reading "$x = $a as null", as a native English speaker, appears to be the same as "$x = null".Well, that's a potential problem with the choice of syntax: "$x = $a as int" could easily be mistaken for "cast $a as int", rather than "assert that $a is int". If you spell out "assert that $a is null", or "assert that $a is int|null", it becomes very surprising for 'hello' to do anything other than fail the assertion.
As I mentioned in the beginning, I see this mostly being used when dealing with mixed types from built-in/library functions, where you have no idea what the actual type is, but when you write the code, you have a reasonable expectation of a set of types and you want to throw if it is unexpected.My argument is that you might have a set of expected types which includes null, *and* want to throw for other, unexpected, values. If "|null" is special-cased to mean "default to null", there's no way to do that.
Right now, the best way to do that is to simply set a function signature and pass the mixed type to the function to have the engine do it for youAnd if you do that, then a value of 'hello' passed to a parameter of type int|null, will throw a TypeError, not give you a null. As I illustrated in my last e-mail, you can even (since PHP 8.2) have a parameter of type null, and get a TypeError for any other value. That may not be useful, but it's entirely logical.
It makes more sense, from a practical programming point-of-view, to simply return the value given if none of the types match.This perhaps is a key part of our difference: when I see "int|bool|null", I don't see any "value given", just three built-in types: int, which has a range of values from PHP_INT_MIN to PHP_INT_MAX; bool, which has two possible values "true" and "false"; and null, which has a single possible value "null". So there are 2**64 + 2 + 1 possible values that meet the constraint, and nothing to specify that one of those is my preferred default if given something unexpected. Regards, -- Rowan Tommins [IMSoP]