Hey all,
I know, it has been quite some time … but it's still well in time for 7.1.
Time to move on and put Union types to vote soon. [In the next 1-2 days]
We have done some changes to the RFC to emphasize the appeal of union types as well as clarified what exactly we will vote on.
If you have more feedback, it's welcome: https://wiki.php.net/rfc/union_types
Thanks,
Bob
Hi Bob,
Bob Weinand wrote:
Hey all,
I know, it has been quite some time … but it's still well in time for 7.1.
Time to move on and put Union types to vote soon. [In the next 1-2 days]
We have done some changes to the RFC to emphasize the appeal of union types as well as clarified what exactly we will vote on.
If you have more feedback, it's welcome: https://wiki.php.net/rfc/union_types
I do wonder if 1-2 days is really sufficient time, given that before
now, the only announced RFC had been substantially less complete.
That aside, the behaviour the RFC now specifies for how weak typing
interacts with union types is frighteningly complicated. I don't see how
it could be anything other than that, but the new complexity this
introduces to PHP is enough for me to vote against this RFC, even
ignoring my other concerns.
Thanks.
Andrea Faulds
https://ajf.me/
That aside, the behaviour the RFC now specifies for how weak typing
interacts with union types is frighteningly complicated. I don't see how
it could be anything other than that, but the new complexity this
introduces to PHP is enough for me to vote against this RFC, even
ignoring my other concerns.
That is rather a long-winded set of rules, I agree. I wonder if it's
possible for the order the union type is defined to be taken into
account? That way you could specify the rule thus:
- The type is tested against each type in the union in turn.
- If weak typing is active, scalar types will be coerced where this
would normally be allowed, and passed over where a TypeError would be
thrown. - If strict typing is active, scalar types will be treated the same as
non-scalar types, and must match precisely.
So:
function f(int | float $number) { echo gettype($number); }
f('10'); // int
function g(float | int $number) { echo gettype($number); }
g('10'); // float
function h(int | Foo | string) { echo gettype($number); }
h('10'); // int
h(new Foo); // object
h('hello'); // string
function i(string | int) { echo gettype($number); }
i('10'); // string
i(10); // string
This last example is the only one that might be a bit surprising - the
"| int" in the type def is actually doing nothing - but there's always
scope for user error.
In strict mode, things would be a bit different, because no coercion is
allowed:
declare(strict_types=1);
function f(int | float $number) { echo gettype($number); }
f('10'); // [!] TypeError
function g(float | int $number) { echo gettype($number); }
g('10'); // [!] TypeError
function h(int | Foo | string) { echo gettype($number); }
h('10'); // string
h(new Foo); // object
h('hello'); // string
function i(string | int) { echo gettype($number); }
i('10'); // string
i(10); // int
It may be that the implementation makes this prohibitively difficult,
but it would certainly be easier to document than the rules currently
proposed.
Regards,
Rowan Collins
[IMSoP]
Am 02.06.2016 um 19:33 schrieb Rowan Collins rowan.collins@gmail.com:
That aside, the behaviour the RFC now specifies for how weak typing
interacts with union types is frighteningly complicated. I don't see how
it could be anything other than that, but the new complexity this
introduces to PHP is enough for me to vote against this RFC, even
ignoring my other concerns.That is rather a long-winded set of rules, I agree. I wonder if it's possible for the order the union type is defined to be taken into account? That way you could specify the rule thus:
- The type is tested against each type in the union in turn.
- If weak typing is active, scalar types will be coerced where this would normally be allowed, and passed over where a TypeError would be thrown.
- If strict typing is active, scalar types will be treated the same as non-scalar types, and must match precisely.
So:
function f(int | float $number) { echo gettype($number); }
f('10'); // intfunction g(float | int $number) { echo gettype($number); }
g('10'); // floatfunction h(int | Foo | string) { echo gettype($number); }
h('10'); // int
h(new Foo); // object
h('hello'); // stringfunction i(string | int) { echo gettype($number); }
i('10'); // string
i(10); // stringThis last example is the only one that might be a bit surprising - the "| int" in the type def is actually doing nothing - but there's always scope for user error.
In strict mode, things would be a bit different, because no coercion is allowed:
declare(strict_types=1);
function f(int | float $number) { echo gettype($number); }
f('10'); // [!] TypeErrorfunction g(float | int $number) { echo gettype($number); }
g('10'); // [!] TypeErrorfunction h(int | Foo | string) { echo gettype($number); }
h('10'); // string
h(new Foo); // object
h('hello'); // stringfunction i(string | int) { echo gettype($number); }
i('10'); // string
i(10); // intIt may be that the implementation makes this prohibitively difficult, but it would certainly be easier to document than the rules currently proposed.
Regards,
Rowan Collins
[IMSoP]
We had that exact idea relatively early, but it exposes other problems… order suddenly matters. You cannot just add "a" type and get the expected results.
E.g.
function f(true | string $foo) { ... }
everything except 0, ±0, "0" and "" would now return true. That's totally WTF. Sure, it's more friendly for people who want to read rules. But it is very bad for intuitivity.
Also:
function i(string | int) { echo gettype($number); }
i('10'); // string
i(10); // string
This. This is especially bad as it has different behaviors in strict and weak mode then. That's a no-go.
The current way is the only viable one, as far as we discovered.
Bob
We had that exact idea relatively early, but it exposes other problems… order suddenly matters. You cannot just add "a" type and get the expected results.
E.g.
function f(true | string $foo) { ... }everything except 0, ±0, "0" and "" would now return true. That's totally WTF. Sure, it's more friendly for people who want to readrules. But it is very bad for intuitivity.
I'm not so sure about that - the RFC already mentions the mnemonic that
"|" means "or", and anyone reading PHP code should be familiar with
short-cutting boolean operators, so this feels kind of natural to me:
"$foo must be true OR a string". Thus "I would prefer it to be true, but
if not, will accept a string".
Also:
function i(string | int) { echo gettype($number); }
i('10'); // string
i(10); // string
This. This is especially bad as it has different behaviors in strict and weak mode then. That's a no-go.
Again, the logic is "I would prefer a string if you can, but an int if
not"; if weak mode tries its best to match that specification, it will
always land on a string coercion.
It's more of a problem the other way around, though, because I'd
forgotten that weak mode is allowed to perform lossy casts:
function f ( int | string $number ) { echo $number; }
f('1a');
In weak mode, this would echo "1", because the int cast succeeds,
lossily. That's a little odd, I admit.
The rules are much clearer in table form, by the way, thumbs up for
that. :) Although it would be nice to point to some documentation of
where these rules were lifted from, given the claim that "they are not
the invention of this proposal".
Looking at them, I see there is one extra rule which doesn't seem to be
consistent with normal weak typing, but is trying very hard to be
lossless, and that's "float -> int (if lossless)" as a separate priority
from "float -> int".
Current loose typing performs no such check:
function f(int $x) { echo gettype($x), ':', $x; }
f(4.5); // integer:4
But the proposal is that this will prefer a string a cast:
function f(int | string $x) { echo gettype($x), ':', $x; }
f(4.5); // string:4.5
If you get rid of this extra rule, the order of checks actually becomes
a simple priority order, regardless of source type: [exact match], int,
float, string, boolean.
If the aim is to be lossless, then perhaps the order could be tweaked to
make lossy casts always lower priority: string, float, int, boolean?
That's technically lossy when passing int(9007199254740993) to "float |
int" because it's above the safe threshold of 2^53, but the current
implementation is already lossy when passing it to "float | string":
function f(float | string $x) { var_export($x); }
f(9007199254740993); // 9007199254740992.0
Regards,
--
Rowan Collins
[IMSoP]
Am 02.06.2016 um 22:25 schrieb Rowan Collins rowan.collins@gmail.com:
We had that exact idea relatively early, but it exposes other problems… order suddenly matters. You cannot just add "a" type and get the expected results.
E.g.
function f(true | string $foo) { ... }everything except 0, ±0, "0" and "" would now return true. That's totally WTF. Sure, it's more friendly for people who want to readrules. But it is very bad for intuitivity.
I'm not so sure about that - the RFC already mentions the mnemonic that "|" means "or", and anyone reading PHP code should be familiar with short-cutting boolean operators, so this feels kind of natural to me: "$foo must be true OR a string". Thus "I would prefer it to be true, but if not, will accept a string".
This is a bitwise or (as in constant flags), where all modes are allowed. It's not a boolean or, whose mnemonic is "||" (not "|").
Also:
function i(string | int) { echo gettype($number); }
i('10'); // string
i(10); // string
This. This is especially bad as it has different behaviors in strict and weak mode then. That's a no-go.Again, the logic is "I would prefer a string if you can, but an int if not"; if weak mode tries its best to match that specification, it will always land on a string coercion.
And again, in strict mode, this would result in an integer - but not in weak mode. (because strict types mandates no cast). This is a no-go, no matter what.
It's more of a problem the other way around, though, because I'd forgotten that weak mode is allowed to perform lossy casts:
function f ( int | string $number ) { echo $number; }
f('1a');In weak mode, this would echo "1", because the int cast succeeds, lossily. That's a little odd, I admit.
The rules are much clearer in table form, by the way, thumbs up for that. :) Although it would be nice to point to some documentation of where these rules were lifted from, given the claim that "they are not the invention of this proposal".
E.g. object->string is possible right now and is preserved (but object->int etc. aren't).
Or a non-numeric string cannot be passed to neither int nor float; thus not possible to pass them to int|float.
Looking at them, I see there is one extra rule which doesn't seem to be consistent with normal weak typing, but is trying very hard to be lossless, and that's "float -> int (if lossless)" as a separate priority from "float -> int".
I'm not strictly opposed here. If more people agree here I may change that.
Current loose typing performs no such check:
function f(int $x) { echo gettype($x), ':', $x; }
f(4.5); // integer:4But the proposal is that this will prefer a string a cast:
function f(int | string $x) { echo gettype($x), ':', $x; }
f(4.5); // string:4.5If you get rid of this extra rule, the order of checks actually becomes a simple priority order, regardless of source type: [exact match], int, float, string, boolean.
If the aim is to be lossless, then perhaps the order could be tweaked to make lossy casts always lower priority: string, float, int, boolean? That's technically lossy when passing int(9007199254740993) to "float | int" because it's above the safe threshold of 2^53, but the current implementation is already lossy when passing it to "float | string":
function f(float | string $x) { var_export($x); }
f(9007199254740993); // 9007199254740992.0
Yes, but this same behavior we have with strict types and we shall adhere to that.
Bob
Regards,
--
Rowan Collins
[IMSoP]
why not try all types weakly from left to right in weak mode?
Rule: if type check would give a fatal error for a type, try next type.
E.g.
function i(int | string $var) { echo gettype($var) . ' ' . $var; }
i('10'); // int 10
i('10a'); // int 10
i('foo'); // string foo
i(10.0); // int 10
i(10); // int 10
Regards
Thomas
Bob Weinand wrote on 02.06.2016 22:56:
Am 02.06.2016 um 22:25 schrieb Rowan Collins rowan.collins@gmail.com:
We had that exact idea relatively early, but it exposes other problems…
order suddenly matters. You cannot just add "a" type and get the expected
results.E.g.
function f(true | string $foo) { ... }everything except 0, ±0, "0" and "" would now return true. That's totally
WTF. Sure, it's more friendly for people who want to readrules. But it is
very bad for intuitivity.I'm not so sure about that - the RFC already mentions the mnemonic that "|"
means "or", and anyone reading PHP code should be familiar with short-cutting
boolean operators, so this feels kind of natural to me: "$foo must be true OR
a string". Thus "I would prefer it to be true, but if not, will accept a
string".This is a bitwise or (as in constant flags), where all modes are allowed. It's
not a boolean or, whose mnemonic is "||" (not "|").Also:
function i(string | int) { echo gettype($number); }
i('10'); // string
i(10); // string
This. This is especially bad as it has different behaviors in strict and weak
mode then. That's a no-go.Again, the logic is "I would prefer a string if you can, but an int if not";
if weak mode tries its best to match that specification, it will always land
on a string coercion.And again, in strict mode, this would result in an integer - but not in weak
mode. (because strict types mandates no cast). This is a no-go, no matter what.It's more of a problem the other way around, though, because I'd forgotten
that weak mode is allowed to perform lossy casts:function f ( int | string $number ) { echo $number; }
f('1a');In weak mode, this would echo "1", because the int cast succeeds, lossily.
That's a little odd, I admit.The rules are much clearer in table form, by the way, thumbs up for that. :)
Although it would be nice to point to some documentation of where these rules
were lifted from, given the claim that "they are not the invention of this
proposal".E.g. object->string is possible right now and is preserved (but object->int
etc. aren't).
Or a non-numeric string cannot be passed to neither int nor float; thus not
possible to pass them to int|float.Looking at them, I see there is one extra rule which doesn't seem to be
consistent with normal weak typing, but is trying very hard to be lossless,
and that's "float -> int (if lossless)" as a separate priority from "float ->
int".I'm not strictly opposed here. If more people agree here I may change that.
Current loose typing performs no such check:
function f(int $x) { echo gettype($x), ':', $x; }
f(4.5); // integer:4But the proposal is that this will prefer a string a cast:
function f(int | string $x) { echo gettype($x), ':', $x; }
f(4.5); // string:4.5If you get rid of this extra rule, the order of checks actually becomes a
simple priority order, regardless of source type: [exact match], int, float,
string, boolean.If the aim is to be lossless, then perhaps the order could be tweaked to make
lossy casts always lower priority: string, float, int, boolean? That's
technically lossy when passing int(9007199254740993) to "float | int" because
it's above the safe threshold of 2^53, but the current implementation is
already lossy when passing it to "float | string":function f(float | string $x) { var_export($x); }
f(9007199254740993); // 9007199254740992.0Yes, but this same behavior we have with strict types and we shall adhere to
that.Bob
Regards,
--
Rowan Collins
[IMSoP]
why not try all types weakly from left to right in weak mode?
Rule: if type check would give a fatal error for a type, try next type.
This is what I suggested, but Bob is insistent that weak mode should
always select the same type as strict mode where there is a valid strict
match, which I guess makes sense.
A strict left-to-right check violates that:
function i(string | int $x) { echo gettype($x); }
i(10); // string in weak mode, int in strict mode
function j(int | string $x) { echo gettype($x); }
j('10'); // int in weak mode, string in strict mode
The main complication that I haven't quite got my head round yet is the
fact that an int->float coercion is allowed in strict mode. So to be
consistent, weak mode needs to privilege that cast:
function k(string | float $x) { echo gettype($x); }
k(10); // float
But in other cases it doesn't seem sensible to privilege float over string:
function l(string | float $x) { echo gettype($x); }
l("1.5a"); // I would expect string, but float would succeed if attempted
Regards,
Rowan Collins
[IMSoP]
Am 3.6.2016 um 12:28 schrieb Rowan Collins rowan.collins@gmail.com:
why not try all types weakly from left to right in weak mode?
Rule: if type check would give a fatal error for a type, try next type.This is what I suggested, but Bob is insistent that weak mode should always select the same type as strict mode where there is a valid strict match, which I guess makes sense.
A strict left-to-right check violates that:
function i(string | int $x) { echo gettype($x); }
i(10); // string in weak mode, int in strict modefunction j(int | string $x) { echo gettype($x); }
j('10'); // int in weak mode, string in strict modeThe main complication that I haven't quite got my head round yet is the fact that an int->float coercion is allowed in strict mode. So to be consistent, weak mode needs to privilege that cast:
function k(string | float $x) { echo gettype($x); }
k(10); // floatBut in other cases it doesn't seem sensible to privilege float over string:
function l(string | float $x) { echo gettype($x); }
l("1.5a"); // I would expect string, but float would succeed if attempted
Exact matches ALWAYS match without coercion.
Emphasis from the RFC:
Primarily, this issue is avoided if a parameter type exactly matches the input type or if PHP is in strict type mode.
Regards,
Rowan Collins
[IMSoP]
Bob
function l(string | float $x) { echo gettype($x); }
l("1.5a"); // I would expect string, but float would succeed if attemptedExact matches ALWAYS match without coercion.
Gah, I keep making that mistake in my examples. I did say I was
struggling to get my head around things! ;)
I'm still missing an explanation of exactly where you derived the
current proposed rules from, though. The RFC just states "these rules
are not an invention of this proposal", but goes on to say "applies PHP
casting rules in a sane way", which implies some decisions were involved
in drawing up these rules.
I think you may be right that the rules are as simple as they can be,
and I don't want to waste too much of your time, but a basic summary of
the reasoning used would be much appreciated.
Regards,
Rowan Collins
[IMSoP]
Am 3.6.2016 um 14:18 schrieb Rowan Collins rowan.collins@gmail.com:
function l(string | float $x) { echo gettype($x); }
l("1.5a"); // I would expect string, but float would succeed if attemptedExact matches ALWAYS match without coercion.
Gah, I keep making that mistake in my examples. I did say I was struggling to get my head around things! ;)
I'm still missing an explanation of exactly where you derived the current proposed rules from, though. The RFC just states "these rules are not an invention of this proposal", but goes on to say "applies PHP casting rules in a sane way", which implies some decisions were involved in drawing up these rules.
I think you may be right that the rules are as simple as they can be, and I don't want to waste too much of your time, but a basic summary of the reasoning used would be much appreciated.
Regards,
Rowan Collins
[IMSoP]
The weak casting rules (i.e. what gets converted how and what can be converted to the other type) are not an invention of the proposal.
The proposal however defines the specific order.
I’ve added a reasoning why not left-to-right now. As this seemed to be asked multiple times.
Also, the specific reasoning why I propose that specific order the rules are in:
Otherwise PHP's casting rules are applied in an order to be as lossless as possible.
That’s really the only motivation behind that.
Bob
maybe you can add a few examples to the type table in the rfc, so everybody knows how it actually works:
function test(int | float | string $a) {var_dump($a);}
test(42.0); // float(42)
test('42'); // string(2) "42"
function test2(int $a) {var_dump($a);}
test2(42.0); // int(42)
test2('42'); // int(42)
function test3(int|bool $a) {var_dump($a);}
test3('foo'); // true
Regards
Thomas
Bob Weinand wrote on 03.06.2016 14:30:
Am 3.6.2016 um 14:18 schrieb Rowan Collins rowan.collins@gmail.com:
function l(string | float $x) { echo gettype($x); }
l("1.5a"); // I would expect string, but float would succeed if attemptedExact matches ALWAYS match without coercion.
Gah, I keep making that mistake in my examples. I did say I was struggling to
get my head around things! ;)I'm still missing an explanation of exactly where you derived the current
proposed rules from, though. The RFC just states "these rules are not an
invention of this proposal", but goes on to say "applies PHP casting rules in
a sane way", which implies some decisions were involved in drawing up these
rules.I think you may be right that the rules are as simple as they can be, and I
don't want to waste too much of your time, but a basic summary of the
reasoning used would be much appreciated.Regards,
Rowan Collins
[IMSoP]The weak casting rules (i.e. what gets converted how and what can be
converted to the other type) are not an invention of the proposal.The proposal however defines the specific order.
I’ve added a reasoning why not left-to-right now. As this seemed to be asked
multiple times.Also, the specific reasoning why I propose that specific order the rules are
in:Otherwise PHP's casting rules are applied in an order to be as lossless as
possible.That’s really the only motivation behind that.
Bob
Am 02.06.2016 um 19:11 schrieb Andrea Faulds ajf@ajf.me:
Hi Bob,
Bob Weinand wrote:
Hey all,
I know, it has been quite some time … but it's still well in time for 7.1.
Time to move on and put Union types to vote soon. [In the next 1-2 days]
We have done some changes to the RFC to emphasize the appeal of union types as well as clarified what exactly we will vote on.
If you have more feedback, it's welcome: https://wiki.php.net/rfc/union_types
I do wonder if 1-2 days is really sufficient time, given that before now, the only announced RFC had been substantially less complete.
That aside, the behaviour the RFC now specifies for how weak typing interacts with union types is frighteningly complicated. I don't see how it could be anything other than that, but the new complexity this introduces to PHP is enough for me to vote against this RFC, even ignoring my other concerns.
Thanks.
Andrea Faulds
https://ajf.me/
Hey,
I think this is more of a presentation problem.
As you say, there's not much a better way to do that.
It's basically our weak casting rules, just applied to the most lossless type available.
It's out weak casting rules which are so complex; the RFCs combination is not particularly complex.
It needs to cover everything, but it's particularly important for the trivial cases to be handled here. Would be quite WTF if you could not pass a float to something accepting int (as one of its types).
I'd welcome every suggestion to make the rules look less scary, because they actually aren't.
Bob
Hey,
I think this is more of a presentation problem.
As you say, there's not much a better way to do that.It's basically our weak casting rules, just applied to the most lossless type available.
It's out weak casting rules which are so complex; the RFCs combination is not particularly complex.
It needs to cover everything, but it's particularly important for the trivial cases to be handled here. Would be quite WTF if you could not pass a float to something accepting int (as one of its types).I'd welcome every suggestion to make the rules look less scary, because they actually aren't.
Bob
Don't you think this matrix would be a better presentation and simpler?
|--------|--------|--------|--------|
| type | #1 | #2 | #3 |
|--------|--------|--------|--------|
| bool | string | int | |
| int | string | float | bool |
| float | string | int | bool |
| object | string | | |
| string | float | int | bool |
|--------|--------|--------|--------|
Everything else is a TypeError.
Note that I try string first all the time. Simply because it makes no
difference for PHP afterwards, e.g.:
function f(string|int $a) {
if ($a == 1) echo 'foo';
}
f(true); // true ~> '1' ~> 1 = foo
Even if the change I applied is not intelligent and is not a good idea
(haven't thought about it deeply), the presentation as a table should
help a lot.
--
Richard "Fleshgrinder" Fussenegger
Am 02.06.2016 um 19:52 schrieb Fleshgrinder php@fleshgrinder.com:
Hey,
I think this is more of a presentation problem.
As you say, there's not much a better way to do that.It's basically our weak casting rules, just applied to the most lossless type available.
It's out weak casting rules which are so complex; the RFCs combination is not particularly complex.
It needs to cover everything, but it's particularly important for the trivial cases to be handled here. Would be quite WTF if you could not pass a float to something accepting int (as one of its types).I'd welcome every suggestion to make the rules look less scary, because they actually aren't.
Bob
Don't you think this matrix would be a better presentation and simpler?
|--------|--------|--------|--------|
| type | #1 | #2 | #3 |
|--------|--------|--------|--------|
| bool | string | int | |
| int | string | float | bool |
| float | string | int | bool |
| object | string | | |
| string | float | int | bool |
|--------|--------|--------|--------|Everything else is a TypeError.
Done that, does it look better now? :-)
Note that I try string first all the time. Simply because it makes no
difference for PHP afterwards, e.g.:function f(string|int $a) {
if ($a == 1) echo 'foo';
}f(true); // true ~> '1' ~> 1 = foo
Even if the change I applied is not intelligent and is not a good idea
(haven't thought about it deeply), the presentation as a table should
help a lot.
There is one reason, it must not be inconsistent with strict types. Switching from strict_types=1 to strict_types=0 shall always work without semantics changes (as long as you don't catch TypeErrors).
--
Richard "Fleshgrinder" Fussenegger
Thanks,
Bob
Done that, does it look better now? :-)
Absolutely in my opinion, looks much simpler now. :)
only if actually numeric, the result of $str + 0
Shouldn't we use is_numeric_string() here?
--
Richard "Fleshgrinder" Fussenegger
Am 02.06.2016 um 20:53 schrieb Fleshgrinder php@fleshgrinder.com:
Done that, does it look better now? :-)
Absolutely in my opinion, looks much simpler now. :)
only if actually numeric, the result of $str + 0
Shouldn't we use is_numeric_string() here?
That's the internal function responsible for the conversion.
Bob
--
Richard "Fleshgrinder" Fussenegger
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/
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.
I actually agree with Andrea on this. There are too many side effects
that are endlessly hard to cover; even if we put all our brain power
together.
I would actually propose to make them always strict. It should not be a
problem for people to simply cast them to the right type. Maybe it even
helps to prevent that people overuse union types (which I am a bit
afraid of too but the feature is too useful). In the end, anyone who
does not like it can always resort to using no type hint at all as they
could in the past.
I know that Bob already said that it is a no go but I do not quite agree
here.
That being said, there is another possible approach. Do nothing in weak
mode.
This might sound weird but the fact that the engine will coerce the
variables anyways as needed would help. Any union type would be mixed
again, like it always was up until now. A problem every PHP developer
knows about and knows how to handle.
In the end it is about relying on super type behavior. If I accept
float|int|string than I should only perform actions on them that all of
them support and I have to check the actual type anyways if I want to
change my behavior based on the specific type. Hence, I can also error
out if I do not receive the type I was expecting. As we are already
doing. :)
--
Richard "Fleshgrinder" Fussenegger
Hi again,
In an attempt to be constructive, I have implemented an alternative
approach to weak scalar type selection here:
https://github.com/krakjoe/php-src/compare/multi-types...TazeTSchnitzel:multi-types-roulette
Where there is no exact scalar type match, this patch uses an algorithm
originally suggested by Nikita, wherein we use rand()
to pick which type
to cast to.
This is much simpler than having a type precedence matrix, and thus is
easier to remember and reason about. The choice of selection function
means that no particular use-case is privileged. Given PHP's scalar
types are roughly equivalent (after all, PHP juggles them freely), this
apparently unorthodox selection process should nonetheless produce
sensible results in most cases.
Please tell me your thoughts!
--
Andrea Faulds
https://ajf.me/
Am 05.06.2016 um 00:55 schrieb Andrea Faulds ajf@ajf.me:
Hi again,
In an attempt to be constructive, I have implemented an alternative approach to weak scalar type selection here:
https://github.com/krakjoe/php-src/compare/multi-types...TazeTSchnitzel:multi-types-roulette
Where there is no exact scalar type match, this patch uses an algorithm originally suggested by Nikita, wherein we use
rand()
to pick which type to cast to.This is much simpler than having a type precedence matrix, and thus is easier to remember and reason about. The choice of selection function means that no particular use-case is privileged. Given PHP's scalar types are roughly equivalent (after all, PHP juggles them freely), this apparently unorthodox selection process should nonetheless produce sensible results in most cases.
Please tell me your thoughts!
--
Andrea Faulds
https://ajf.me/ https://ajf.me/
This is the holy grail I was hunting for so long!
All my computational problems can be solved by a simple rand()
!
In case nobody objects I'm going to merge that straight ahead into php-src 7.0 (no need for master-only, after all it's no real BC break!).
Thanks a very much!!!
Bob
Hi Andrea, Bob
-----Ursprüngliche Nachricht-----
Von: Bob Weinand [mailto:bobwei9@hotmail.com]
Gesendet: Sonntag, 5. Juni 2016 01:00
An: Andrea Faulds
Cc: internals@lists.php.net
Betreff: Re: [PHP-DEV] Re: [RFC] [PRE-VOTE] Union typesAm 05.06.2016 um 00:55 schrieb Andrea Faulds ajf@ajf.me:
Hi again,
In an attempt to be constructive, I have implemented an alternative approach to weak scalar type selection here:
https://github.com/krakjoe/php-src/compare/multi-types...TazeTSchnitzel:multi-types-roulette
Where there is no exact scalar type match, this patch uses an algorithm originally suggested by Nikita, wherein we
use
rand()
to pick which type to cast to.This is much simpler than having a type precedence matrix, and thus is easier to remember and reason about. The
choice
of selection function means that no particular use-case is privileged. Given PHP's scalar types are roughly equivalent
(after
all, PHP juggles them freely), this apparently unorthodox selection process should nonetheless produce sensible
results in
most cases.Please tell me your thoughts!
--
Andrea Faulds
https://ajf.me/ https://ajf.me/
This is the holy grail I was hunting for so long!All my computational problems can be solved by a simple
rand()
!In case nobody objects I'm going to merge that straight ahead into php-src 7.0 (no need for master-only, after all
it's no real
BC break!).Thanks a very much!!!
Bob
Not sure if this is a clever idea. I haven't read all emails so bear with me if this is more than clear. What happens
with the following?
function foo(int | string $a) {
if ($a == "") {
//a
} else {
//b
}
}
foo(false);
What branch is taken? Is it taken randomly?
Hi Andrea, Bob
-----Ursprüngliche Nachricht-----
Von: Bob Weinand [mailto:bobwei9@hotmail.com]
Gesendet: Sonntag, 5. Juni 2016 01:00
An: Andrea Faulds
Cc: internals@lists.php.net
Betreff: Re: [PHP-DEV] Re: [RFC] [PRE-VOTE] Union typesAm 05.06.2016 um 00:55 schrieb Andrea Faulds ajf@ajf.me:
Hi again,
In an attempt to be constructive, I have implemented an alternative
approach to weak scalar type selection here:https://github.com/krakjoe/php-src/compare/multi-types...TazeTSchnitzel:multi-types-roulette
Where there is no exact scalar type match, this patch uses an
algorithm originally suggested by Nikita, wherein we
use
rand()
to pick which type to cast to.This is much simpler than having a type precedence matrix, and thus is
easier to remember and reason about. The
choice
of selection function means that no particular use-case is privileged.
Given PHP's scalar types are roughly equivalent
(after
all, PHP juggles them freely), this apparently unorthodox selection
process should nonetheless produce sensible
results in
most cases.Please tell me your thoughts!
--
Andrea Faulds
https://ajf.me/ https://ajf.me/
This is the holy grail I was hunting for so long!All my computational problems can be solved by a simple
rand()
!In case nobody objects I'm going to merge that straight ahead into
php-src 7.0 (no need for master-only, after all
it's no real
BC break!).Thanks a very much!!!
Bob
Not sure if this is a clever idea. I haven't read all emails so bear with
me if this is more than clear. What happens
with the following?function foo(int | string $a) {
if ($a == "") {
//a
} else {
//b
}
}foo(false);
What branch is taken? Is it taken randomly?
In this case, A will always be taken, because of the loose comparison (==).
Basically:
var_dump("" == ""); // bool(true)
var_dump(0 == ""); // bool(true)
var_dump(false == ""); // bool(true)
-----Ursprüngliche Nachricht-----
Von: Ryan Pallas [mailto:derokorian@gmail.com]
Gesendet: Sonntag, 5. Juni 2016 14:22
An: Robert Stoll
Cc: Bob Weinand; Andrea Faulds; internals@lists.php.net
Betreff: Re: [PHP-DEV] Re: [RFC] [PRE-VOTE] Union typesHi Andrea, Bob
-----Ursprüngliche Nachricht-----
Von: Bob Weinand [mailto:bobwei9@hotmail.com]
Gesendet: Sonntag, 5. Juni 2016 01:00
An: Andrea Faulds
Cc: internals@lists.php.net
Betreff: Re: [PHP-DEV] Re: [RFC] [PRE-VOTE] Union typesAm 05.06.2016 um 00:55 schrieb Andrea Faulds ajf@ajf.me:
Hi again,
In an attempt to be constructive, I have implemented an alternative approach to weak scalar type selection
here:https://github.com/krakjoe/php-src/compare/multi-types...TazeTSchnitzel:multi-types-roulette
Where there is no exact scalar type match, this patch uses an algorithm originally suggested by Nikita, wherein
we
use
rand()
to pick which type to cast to.This is much simpler than having a type precedence matrix, and thus is easier to remember and reason about.
The
choice
of selection function means that no particular use-case is privileged. Given PHP's scalar types are roughly
equivalent
(after
all, PHP juggles them freely), this apparently unorthodox selection process should nonetheless produce sensible
results in
most cases.Please tell me your thoughts!
--
Andrea Faulds
https://ajf.me/ https://ajf.me/
This is the holy grail I was hunting for so long!All my computational problems can be solved by a simple
rand()
!In case nobody objects I'm going to merge that straight ahead into php-src 7.0 (no need for master-only, after all
it's no real
BC break!).Thanks a very much!!!
Bob
Not sure if this is a clever idea. I haven't read all emails so bear with me if this is more than clear. What happens
with the following?function foo(int | string $a) {
if ($a == "") {
//a
} else {
//b
}
}foo(false);
What branch is taken? Is it taken randomly?
In this case, A will always be taken, because of the loose comparison (==). Basically:
var_dump("" == ""); // bool(true)
var_dump(0 == ""); // bool(true)
var_dump(false == ""); // bool(true)
My fault, so what about
if ($a === "") {
}
Hi!
Where there is no exact scalar type match, this patch uses an algorithm
originally suggested by Nikita, wherein we userand()
to pick which type
to cast to.
Random type conversion. For people that are bored with plaid old
deterministic code bugs.
Stas Malyshev
smalyshev@gmail.com
I know, it has been quite some time … but it's still well in time for 7.1.
Time to move on and put Union types to vote soon. [In the next 1-2 days]
We have done some changes to the RFC to emphasize the appeal of union types as well as clarified what exactly we will vote on.
If you have more feedback, it's welcome: https://wiki.php.net/rfc/union_types
Would it be COMPLETELY antisocial at this point to ask for a secondary
vote on this RFC for adding the "num" type which is an alias for (int
| float) ?
-Sara
Am 03.06.2016 um 03:41 schrieb Sara Golemon pollita@php.net:
I know, it has been quite some time … but it's still well in time for 7.1.
Time to move on and put Union types to vote soon. [In the next 1-2 days]
We have done some changes to the RFC to emphasize the appeal of union types as well as clarified what exactly we will vote on.
If you have more feedback, it's welcome: https://wiki.php.net/rfc/union_types
Would it be COMPLETELY antisocial at this point to ask for a secondary
vote on this RFC for adding the "num" type which is an alias for (int
| float) ?-Sara
Not anti-social, but I won't add it...
The main concern is that it is an extra BC break (num is not too rare for class/namespace names I'd argue).
Additionally it warrants extra discussion. Num, Numeric? Which one?
Also proper elaboration why we should add it or not would have to be done...
I think this should be an own RFC thus.
Bob
Hi!
Would it be COMPLETELY antisocial at this point to ask for a secondary
vote on this RFC for adding the "num" type which is an alias for (int
| float) ?
I think piling up votes that are not necessary for the specific RFC's
topic is not right. We can always have another RFC :)
--
Stas Malyshev
smalyshev@gmail.com
Hey all,
I know, it has been quite some time … but it's still well in time for 7.1.
Time to move on and put Union types to vote soon. [In the next 1-2 days]
We have done some changes to the RFC to emphasize the appeal of
union types as well as clarified what exactly we will vote on.
If you have more feedback, it's welcome: https://wiki.php.net/rfc/union_types
Thanks,
Bob
Thank you (and Levi) for this RFC! I can't vote, but I'd like to
weigh in with my userland perspective.
Just last week I was working with several functions that accept
array|Traversable and array|string parameters. Union types would
make it easier to document these functions, as well as eliminate the
boilerplate code currently required to validate their parameters.
I am also in favor of the proposal to replace ?QuestionMarkNullables
with union|null. Having a single way to declare nullable types will
keep the reflection API simpler, make things easier for static
analysis tools, and prevent developers from having to wonder which
syntax they should use for a given function. This would be a big win
for language consistency, and this is probably our only chance to
make the change without breaking BC.
Theodore Brown
I am also in favor of the proposal to replace ?QuestionMarkNullables
with union|null. Having a single way to declare nullable types will
keep the reflection API simpler, make things easier for static
analysis tools, and prevent developers from having to wonder which
syntax they should use for a given function. This would be a big win
for language consistency, and this is probably our only chance to
make the change without breaking BC.
I am actually only in favor of disallowing ?T1|T2 in favor of T1|T2|null
but not ?T because it's shorter and I do not see a problem with that.
--
Richard "Fleshgrinder" Fussenegger