Hi,
Bob Weinand wrote:
I think this is more of a presentation problem.
As you say, there's not much a better way to do that.
Well, that's why I question if we should do it at all, if there's no way
to do it without this complexity.
It's basically our weak casting rules, just applied to the most lossless type
available.
Right, I think I can see the logic to that. But I don't think it works
out so well in practice.
Consider the hypothetical example `int|string`. Under your rules, if I
pass a float to that parameter, it becomes a string.
That makes sense if the function just wants a number, in integer or
string format, for that parameter.
But what if the function wants either a number (in integer format) or a
string, and does different things depending on which of these it gets?
Now the proposed weak typing rules are broken.
A real-world example of this is array indices. Given array keys can be
either integers or strings, then if we wanted to take an array key as a
parameter, surely we'd use the type declaration `int|string`, right?
Well, it turns out that in PHP, $a[1.5] is not equivalent to $a["1.5"],
it's equivalent to $a[1]. Yet, if I pass 1.5 to an `int|string` typed
parameter, I'd get "1.5". So, we'd end up with an inconsistency:
<?php
function fetchValueForKeyInArray(array $array, int|string $key) {
return $array[$key];
}
$arr = ["1.5" => "wrong", 1 => "right"];
var_dump($arr[1.5]); // string(5) "right"
var_dump(fetchValueForKeyInArray($arr, $key)); // string(5) "wrong"
?>
Trying to do the most "lossless" conversion works for some use-cases,
but it doesn't work for others, and it doesn't necessarily match
existing behaviour in other parts of PHP.
This is a wider concern for me, in that union types have two different
use cases: where you want to accept any type of some ad-hoc super-type
(int|float as an ad-hoc "number", array|\Traversable as an ad-hoc "thing
that can be foreach()'d), but there is also the use case of where you
want to accept two completely unrelated types (int|string for two kinds
of array key, int|array, etc.) and perhaps do different things depending
on the types given.
This latter use case strikes me as making a function overly complicated
(make a separate function instead), and it interacts poorly with weak
typing (if PHP decides to give you a different type, you get a
completely different code path!), yet the addition of union types
encourages writing such functions! (At least, for people who want to use
type declarations throughout their code.)
Moreover, I think whatever weak typing precedence rules we choose, they
can only work reasonably for one or the other of these use-cases, not
both. I think that `int|string` is a succint example of that: whichever
ruleset we choose, it will do the wrong thing for some usecase, we're
just deciding which one to privilege.
It's out weak casting rules which are so complex; the RFCs combination is not
particularly complex.
The RFC uses simple rules, but the resulting behaviour is complex.
PHP's weak typing rules are complicated enough as they are, and cause
enough problem, without the addition of yet more rules, this time for
how weak typing should work within union types.
I don't think 2 days is enough time to discuss this before voting on it.
Thanks.
--
Andrea Faulds
https://ajf.me/
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php