Hi Internals!
I prepared a PR to add the scalar
pseudo-type to PHP after the
discussions around adding a mixed
pseudo-type. I strongly believe that
it makes sense to provide the most common primitive union types with
handy aliases even if we are going to add union types in the future to PHP.
https://github.com/php/php-src/pull/2987
I added support for parameter type covariance and return type
contravariance to make it as useful in daily development as possible.
I will provide the RFC write-up asap at:
https://wiki.php.net/rfc/scalar-pseudo-type
--
Richard "Fleshgrinder" Fussenegger
Hi Internals!
I prepared a PR to add the
scalar
pseudo-type to PHP after the
discussions around adding amixed
pseudo-type. I strongly believe that
it makes sense to provide the most common primitive union types with
handy aliases even if we are going to add union types in the future to PHP.https://github.com/php/php-src/pull/2987
I added support for parameter type covariance and return type
contravariance to make it as useful in daily development as possible.I will provide the RFC write-up asap at:
I think this RFC could benefit from displaying some use-cases for this type
annotation. I can't recall any recent instance where I would have found
this specific type combination useful, though I'm sure there are good
examples.
I also wonder whether in weak typing mode, scalar should also accept
__toString objects (and cast them to string), similarly to how a
bool|int|float|string union would behave.
Nikita
I think this RFC could benefit from displaying some use-cases for this type
annotation. I can't recall any recent instance where I would have found
this specific type combination useful, though I'm sure there are good
examples.I also wonder whether in weak typing mode, scalar should also accept
__toString objects (and cast them to string), similarly to how a
bool|int|float|string union would behave.Nikita
I will extend it with some examples.
I guess that that would be useful in weak mode. Will try to hack it in.
I am currently also working on another RFC that adds a Convertible
interface with a single into
method that has its return type set to
scalar
where objects can opt-in to become scalar
compatible. Even in
strict mode. The idea is that the object's into
method is
automatically called by the engine and the receiver gets the canonical
scalar
value that represents the object. This is specifically useful
for value objects that often represent single scalar
values in a type
safe manner.
--
Richard "Fleshgrinder" Fussenegger
I will extend it with some examples.
I guess that that would be useful in weak mode. Will try to hack it in.
I am currently also working on another RFC that adds a
Convertible
interface with a singleinto
method that has its return type set to
scalar
where objects can opt-in to becomescalar
compatible. Even in
strict mode. The idea is that the object'sinto
method is
automatically called by the engine and the receiver gets the canonical
scalar
value that represents the object. This is specifically useful
for value objects that often represent singlescalar
values in a type
safe manner.
Added some examples but I will stop now working on anything because
people already start complaining again. Contributing to PHP is like
kicking a combat dog ... let's wait for some support for this feature in
general first.
--
Richard "Fleshgrinder" Fussenegger
Am 24.12.2017 um 15:34 schrieb Fleshgrinder:
I prepared a PR to add the
scalar
pseudo-type to PHP after the
discussions around adding amixed
pseudo-type. I strongly believe that
it makes sense to provide the most common primitive union types with
handy aliases even if we are going to add union types in the future to PHP.
Thank you, Richard, for working on this.
I spent a lot of time this year introducing scalar type declarations into
existing code bases. In quite a few cases I was not able to do so because
the existing code works with parameters that can be of two or more scalar
types. With PHP 7.2, I can only document this outside of the code using
@param annotations (until the code has been refactored to work with only
one parameter type).
With a "scalar" type declaration I would not have to fall back to @param
annotations and could express the type in actual syntax.
Am 24.12.2017 um 15:34 schrieb Fleshgrinder:
I prepared a PR to add the
scalar
pseudo-type to PHP after the
discussions around adding amixed
pseudo-type. I strongly believe that
it makes sense to provide the most common primitive union types with
handy aliases even if we are going to add union types in the future to
PHP.Thank you, Richard, for working on this.
I spent a lot of time this year introducing scalar type declarations into
existing code bases. In quite a few cases I was not able to do so because
the existing code works with parameters that can be of two or more scalar
types. With PHP 7.2, I can only document this outside of the code using
@param annotations (until the code has been refactored to work with only
one parameter type).With a "scalar" type declaration I would not have to fall back to @param
annotations and could express the type in actual syntax.
If you want just two of those and not all, you'd still have to use PHPdoc
for those?
IIRC you voted against union types, which would be a better fit in that
case?
Regards, Niklas
Am 26.12.2017 um 14:38 schrieb Sebastian Bergmann:
Thank you, Richard, for working on this.
I spent a lot of time this year introducing scalar type declarations into
existing code bases. In quite a few cases I was not able to do so because
the existing code works with parameters that can be of two or more scalar
types. With PHP 7.2, I can only document this outside of the code using
@param annotations (until the code has been refactored to work with only
one parameter type).With a "scalar" type declaration I would not have to fall back to @param
annotations and could express the type in actual syntax
https://wiki.php.net/rfc/union_types
sebastian (sebastian) - voted NO
would you mind to explain this?
Am 26.12.2017 um 16:46 schrieb lists@rhsoft.net:
would you mind to explain this?
"Foo|Bar", "array|string", etc. (still) make no sense to me.
"scalar" makes sense to me although it is but an alias for
"bool|float|int|string".
Am 26.12.2017 um 16:56 schrieb Sebastian Bergmann:
Am 26.12.2017 um 16:46 schrieb lists@rhsoft.net:
would you mind to explain this?
"Foo|Bar", "array|string", etc. (still) make no sense to me.
"scalar" makes sense to me although it is but an alias for
"bool|float|int|string".
honestly that makes no sense for me
with "float|int" you would have no need for dozens of aliases, in that
case "numeric" when i expect some number but not a string and not a
boolean return by a strpos()
or similar functions
also i have function which allows "int|array" where the internal logic
loops the array internally but i don't want a random boolean or string there
it's all about express accepted types as strict as possible in code to
find errors early and in strict_types mode even the caller which needs
to be fixed in the stack-trace
Am 26.12.2017 um 16:46 schrieb lists@rhsoft.net:
would you mind to explain this?
"Foo|Bar", "array|string", etc. (still) make no sense to me.
"scalar" makes sense to me although it is but an alias for
"bool|float|int|string".
If I may, I think the argument has always been that
- Foo & Bar makes total sense
- int|float makes total sense
- int & string is illogical so wouldn't matter anyway
- Foo|Bar rarely makes sense but may sometimes, and its legality is an
acceptable trade-off to get the first two.
In previous discussions it always seemed that people fixated on case 4 and
ignored the usefulness of cases 1 and 2.
--Larry Garfield
Am 26.12.2017 um 19:18 schrieb Larry Garfield:
Am 26.12.2017 um 16:46 schrieb lists@rhsoft.net:
would you mind to explain this?
"Foo|Bar", "array|string", etc. (still) make no sense to me.
"scalar" makes sense to me although it is but an alias for
"bool|float|int|string".If I may, I think the argument has always been that
- Foo & Bar makes total sense
- int|float makes total sense
- int & string is illogical so wouldn't matter anyway
not true
function x(int|string $x)
{
$x = (int)$x;
}
$x can be simply from a database simply because without
MYSQLI_OPT_INT_AND_FLOAT_NATIVE
which is sadly not default you get
everything back as string and so you can place the casting in the
function instead of every single caller in strict_types mode while you
don#t accept object, array or boolean here
why not cast a boolean here?
in case of the result of strpos()
in the caller is false it likely
should not call the function anyways but handle that error itself
- Foo|Bar rarely makes sense but may sometimes, and its legality is an
acceptable trade-off to get the first two.In previous discussions it always seemed that people fixated on case 4 and
ignored the usefulness of cases 1 and 2
as this is a programming language it's up to the user who writes the
code in the language what is useful for his case - so if one is fixated
on 3 or 4 because they would become possible the argumentation is
strange anyways and "etc. (still) make no sense to me" is a personal
opinion which hardly justifies a downvote at all unless there are real
technical reasons
in my total valid usecase of 3) above in strict_types mode i must ommit
the typehint at all or modify every single caller and that alone would
justify unions
Am 26.12.2017 um 19:18 schrieb Larry Garfield:
If I may, I think the argument has always been that
- Foo & Bar makes total sense
- int|float makes total sense
- int & string is illogical so wouldn't matter anyway
not true
function x(int|string $x)
{
$x = (int)$x;
}
I think there's a misunderstanding here. Some previous proposals for "union types" also included "intersection types", so that you could assert "parameter must implement both of these interfaces". So 'Foo|Bar $x' would mean '$x instanceof Foo || $x instanceof Bar' and 'Foo&Bar $x' would mean '$x instanceof Foo && $x instanceof Bar'.
I presume that's what Larry means by "int & string is illogical", because it would translate to "is_int($x) && is_string($x)", which is false for all values of $x. This is different from "int | string", which, as you say, might have valid uses.
Regards,
--
Rowan Collins
[IMSoP]
On 26 December 2017 18:35:29 GMT+00:00, "lists@rhsoft.net"
lists@rhsoft.net wrote:Am 26.12.2017 um 19:18 schrieb Larry Garfield:
If I may, I think the argument has always been that
- Foo & Bar makes total sense
- int|float makes total sense
- int & string is illogical so wouldn't matter anyway
not true
function x(int|string $x)
{$x = (int)$x;
}
I think there's a misunderstanding here. Some previous proposals for "union
types" also included "intersection types", so that you could assert
"parameter must implement both of these interfaces". So 'Foo|Bar $x' would
mean '$x instanceof Foo || $x instanceof Bar' and 'Foo&Bar $x' would mean
'$x instanceof Foo && $x instanceof Bar'.I presume that's what Larry means by "int & string is illogical", because it
would translate to "is_int($x) && is_string($x)", which is false for all
values of $x. This is different from "int | string", which, as you say,
might have valid uses.Regards,
Correct. Union types I've always seen presented as offering both union and
intersection. There are cases where union is great, and where it's kinda
silly. There are cases where intersect is great, and where it's kinda silly.
Most of the anti- arguments I've seen for "union types" have fixated on "int &&
string is meaningless, and Foo || Bar is bad design, so union types are bad!"
Entirely ignoring the flip side, which is int || string (valid use cases) and
Foo && Bar (many many valid use cases).
--Larry Garfield
Am 29.12.2017 um 00:21 schrieb Larry Garfield:
Correct. Union types I've always seen presented as offering both union and
intersection. There are cases where union is great, and where it's kinda
silly. There are cases where intersect is great, and where it's kinda silly.Most of the anti- arguments I've seen for "union types" have fixated on "int &&
string is meaningless, and Foo || Bar is bad design, so union types are bad!"
Entirely ignoring the flip side, which is int || string (valid use cases) and
Foo && Bar (many many valid use cases)
well, that explains why the same person which hase a usecase for a
"scalar" pseudo-type donw-votes https://wiki.php.net/rfc/union_types but
it makes his vote not logical at all
frankly the only valid reasons to down-vote something should be
technical ones which matters for the PHP core itself and not "i don't
understand a feature hence nobody should have it"
wrote in message news:4b55eed1-8656-ff70-e4e9-ad5e40213405@rhsoft.net...
Am 29.12.2017 um 00:21 schrieb Larry Garfield:
Correct. Union types I've always seen presented as offering both union
and
intersection. There are cases where union is great, and where it's kinda
silly. There are cases where intersect is great, and where it's kinda
silly.Most of the anti- arguments I've seen for "union types" have fixated on
"int &&
string is meaningless, and Foo || Bar is bad design, so union types are
bad!"
Entirely ignoring the flip side, which is int || string (valid use cases)
and
Foo && Bar (many many valid use cases)well, that explains why the same person which hase a usecase for a "scalar"
pseudo-type donw-votes https://wiki.php.net/rfc/union_types but it makes
his vote not logical at allfrankly the only valid reasons to down-vote something should be technical
ones which matters for the PHP core itself and not "i don't understand a
feature hence nobody should have it"
You are missing the point. If an RFC is so badly written that someone does
not understand it, or understand what benefits it is supposed to provide,
then there is no point in up-voting it. You may contrive a use case where it
provides a small benefit, but if that use case is so limited or so obscure
that it does not apply to a significant number of developers then that RFC
should be voted down simply because it does not provide any significant
benefits.
--
Tony Marston
On 26 December 2017 18:35:29 GMT+00:00, "lists@rhsoft.net"
lists@rhsoft.net wrote:Am 26.12.2017 um 19:18 schrieb Larry Garfield:
If I may, I think the argument has always been that
- Foo & Bar makes total sense
- int|float makes total sense
- int & string is illogical so wouldn't matter anyway
not true
function x(int|string $x)
{$x = (int)$x;
}
I think there's a misunderstanding here. Some previous proposals for "union
types" also included "intersection types", so that you could assert
"parameter must implement both of these interfaces". So 'Foo|Bar $x' would
mean '$x instanceof Foo || $x instanceof Bar' and 'Foo&Bar $x' would mean
'$x instanceof Foo && $x instanceof Bar'.I presume that's what Larry means by "int & string is illogical", because it
would translate to "is_int($x) && is_string($x)", which is false for all
values of $x. This is different from "int | string", which, as you say,
might have valid uses.Regards,
Correct. Union types I've always seen presented as offering both union and
intersection. There are cases where union is great, and where it's kinda
silly. There are cases where intersect is great, and where it's kinda silly.Most of the anti- arguments I've seen for "union types" have fixated on "int &&
string is meaningless, and Foo || Bar is bad design, so union types are bad!"
Entirely ignoring the flip side, which is int || string (valid use cases) and
Foo && Bar (many many valid use cases).--Larry Garfield
What is the use case for int|float
? I mean, if f is able to process a
float
than f is able to process an int
and since int
is already
automatically changed to a float
, well, you're done.
The only situation (I can think of) where this might make a difference
is while formatting them. However, in those cases one usually wants to
accept many more types (or better yet, let the type format itself).
I think that the union RFC was missing proper rules for disjunction (and
conjunction if intersection were to be included) as well as information
on disjointness. The latter would be important for exhaustive switches
that are enforced by the runtime (we'd probably need new keywords here,
e.g. match
+ is
).
--
Richard "Fleshgrinder" Fussenegger
What is the use case for
int|float
? I mean, if f is able to process a
float
than f is able to process anint
and sinceint
is already
automatically changed to afloat
, well, you're done.
I think it is somewhat tedious if we discuss every possible pair of types, just as it would be somewhat messy if we added a new keyword for every combination we found a use case for. The beauty of a general-purpose syntax is precisely that a user can use whatever combination they need, and not use combinations they don't need. I'm sure there are plenty of nonsensical or redundant checks that can be expressed in other parts of the language, but that doesn't mean those language constructs are useless or damaging.
Regards,
--
Rowan Collins
[IMSoP]
On 29 December 2017 12:08:16 GMT+00:00, Fleshgrinder
php@fleshgrinder.com wrote:What is the use case for
int|float
? I mean, if f is able to
process afloat
than f is able to process anint
and since
int
is already automatically changed to afloat
, well, you're
done.I think it is somewhat tedious if we discuss every possible pair of
types, just as it would be somewhat messy if we added a new keyword
for every combination we found a use case for. The beauty of a
general-purpose syntax is precisely that a user can use whatever
combination they need, and not use combinations they don't need. I'm
sure there are plenty of nonsensical or redundant checks that can be
expressed in other parts of the language, but that doesn't mean those
language constructs are useless or damaging.Regards,
I agree and I do not intend to do so, I actually am not even questioning
the usefulness of union and intersection types. I am more curious in
regards to providing a number
type. Seems useless to me.
--
Richard "Fleshgrinder" Fussenegger
On 29 December 2017 12:08:16 GMT+00:00, Fleshgrinder
php@fleshgrinder.com wrote:What is the use case for
int|float
? I mean, if f is able to
process afloat
than f is able to process anint
and since
int
is already automatically changed to afloat
, well, you're
done.I think it is somewhat tedious if we discuss every possible pair of
types, just as it would be somewhat messy if we added a new keyword
for every combination we found a use case for. The beauty of a
general-purpose syntax is precisely that a user can use whatever
combination they need, and not use combinations they don't need. I'm
sure there are plenty of nonsensical or redundant checks that can be
expressed in other parts of the language, but that doesn't mean those
language constructs are useless or damaging.Regards,
I agree and I do not intend to do so, I actually am not even questioning
the usefulness of union and intersection types. I am more curious in
regards to providing anumber
type. Seems useless to me.--
Richard "Fleshgrinder" Fussenegger--
I'm not sure "number" as a predefined type union is necessary but int|float would allow a method to accept either in strict mode, and as you said it would also be useful for eg a formatting function.
On 29 December 2017 12:08:16 GMT+00:00, Fleshgrinder
php@fleshgrinder.com wrote:
What is the use case for
int|float
? I mean, if f is able to
process afloat
than f is able to process anint
and since
int
is already automatically changed to afloat
, well, you're
done.I think it is somewhat tedious if we discuss every possible pair of
types, just as it would be somewhat messy if we added a new keyword
for every combination we found a use case for. The beauty of a
general-purpose syntax is precisely that a user can use whatever
combination they need, and not use combinations they don't need. I'm
sure there are plenty of nonsensical or redundant checks that can be
expressed in other parts of the language, but that doesn't mean those
language constructs are useless or damaging.Regards,
I agree and I do not intend to do so, I actually am not even questioning
the usefulness of union and intersection types. I am more curious in
regards to providing anumber
type. Seems useless to me.I'm not sure "number" as a predefined type union is necessary but int|float
would allow a method to accept either in strict mode, and as you said it
would also be useful for eg a formatting function.
I think he's referring more to the fact that int -> float is the only auto-cast
allowed in strict mode, so it's not a great example of where a scalar union
type would be useful. Which is a fair point.
Nonetheless, it sounds like we're all saying the same thing:
The fact that there are cases where a union or intersection declaration would
be nonsensical or arguably poor design doesn't change the fact that there are
plenty of cases where they would be entirely sensible and very good design,
and building a bunch of one-off custom unions (scalar, number, mixed,
iterable, etc.) is a poor substitute.
So to those who voted against allowing Foo && Bar as a declaration, why? More
specifically, what would get you to change your vote to allow general union/
intersection types, so we don't need all of these one-offs?
--Larry Garfield
Am 29.12.2017 um 13:08 schrieb Fleshgrinder:
What is the use case for
int|float
? I mean, if f is able to process a
float
than f is able to process anint
and sinceint
is already
automatically changed to afloat
, well, you're done
just read the mass of bugreports caused by float answered with the
default paragraph below and you know why you don't want your int-values
silently converted to a float
7 may become to 7.000000000000000001 or something similar and "$x === 7"
may also fail wile the argument was int 7
Floating point values have a limited precision. Hence a value might
not have the same string representation after any processing. That also
includes writing a floating point value in your script and directly
printing it without any mathematical operations.
If you would like to know more about "floats" and what IEEE
754 is, read this:
http://www.floating-point-gui.de/
Am 29.12.2017 um 13:08 schrieb Fleshgrinder:
What is the use case for
int|float
? I mean, if f is able to process a
float
than f is able to process anint
and sinceint
is already
automatically changed to afloat
, well, you're donejust read the mass of bugreports caused by float answered with the
default paragraph below and you know why you don't want your int-values
silently converted to a float7 may become to 7.000000000000000001 or something similar and "$x === 7"
may also fail wile the argument was int 7
Floating point values have a limited precision. Hence a value might
not have the same string representation after any processing. That also
includes writing a floating point value in your script and directly
printing it without any mathematical operations.If you would like to know more about "floats" and what IEEE
754 is, read this:
http://www.floating-point-gui.de/
Obviously but this does not answer anything. You expect an int or a
float, hence, you need to be prepared to handle floats. Your 7 example
is the best illustration. You need to handle those situations in your
script with the appropriate strategy for your domain (rounding,
truncation, floor, ...).
--
Richard "Fleshgrinder" Fussenegger
Am 29.12.2017 um 18:18 schrieb Fleshgrinder:
Am 29.12.2017 um 13:08 schrieb Fleshgrinder:
What is the use case for
int|float
? I mean, if f is able to process a
float
than f is able to process anint
and sinceint
is already
automatically changed to afloat
, well, you're donejust read the mass of bugreports caused by float answered with the
default paragraph below and you know why you don't want your int-values
silently converted to a float7 may become to 7.000000000000000001 or something similar and "$x === 7"
may also fail wile the argument was int 7
Floating point values have a limited precision. Hence a value might
not have the same string representation after any processing. That also
includes writing a floating point value in your script and directly
printing it without any mathematical operations.If you would like to know more about "floats" and what IEEE
754 is, read this:
http://www.floating-point-gui.de/Obviously but this does not answer anything. You expect an int or a
float, hence, you need to be prepared to handle floats. Your 7 example
is the best illustration. You need to handle those situations in your
script with the appropriate strategy for your domain (rounding,
truncation, floor, ...)
no, when i accept "int|float" i don't get something converted at all and
i can handle the cases different - when it#s silently casted to a float
i have no way to know it was a int at call time
no, when i accept "int|float" i don't get something converted at all and
i can handle the cases different - when it#s silently casted to a float
i have no way to know it was a int at call time
Again, obviously, the question remains, why would you care? Please
provide convincing arguments as this would become the body of the
corresponding RFC. Adding that type is super simple, the question is why
is it so super handy?
PS: Despite union and intersection types, they automatically allow this
form and I repeat that I am totally in favor of them. The question is
truly about number
only which would accept int
and float
(no
string
).
--
Richard "Fleshgrinder" Fussenegger
Am 29.12.2017 um 18:24 schrieb Fleshgrinder:
no, when i accept "int|float" i don't get something converted at all and
i can handle the cases different - when it#s silently casted to a float
i have no way to know it was a int at call timeAgain, obviously, the question remains, why would you care? Please
provide convincing arguments as this would become the body of the
corresponding RFC. Adding that type is super simple, the question is why
is it so super handy?
beause i can insert the value to a int field of a database without
additional costs, because i can write faster code-paths dealing with
integers which don't have all the magic problems of floats and so on
PS: Despite union and intersection types, they automatically allow this
form and I repeat that I am totally in favor of them. The question is
truly aboutnumber
only which would acceptint
andfloat
(no
string
)
nobody needs "number" - the problem is that people downvoted the union
RFC for no good reasons and "i don't see a benefit, i don't like it" are
no valid reasons to do so - and to the other guy which asked why someone
should up-vote for something he don't understand - nobody says that - i
didn't know that voting for each and any RFC is mandatory - so just
ignore it when you don#t care instead downvote
Am 29.12.2017 um 18:30 schrieb lists@rhsoft.net:
"i don't see a benefit, i don't like it" are no valid reasons to do so
I strongly disagree. If a majority of people do not see the benefit of a
syntax change then the syntax should not be changed. A change to the
syntax of PHP has a ripple effect throughout the library and tools
ecosystem of PHP. This is a cost. This cost needs to be justified by a
benefit.
Am 30.12.2017 um 18:15 schrieb Sebastian Bergmann:
Am 29.12.2017 um 18:30 schrieb lists@rhsoft.net:
"i don't see a benefit, i don't like it" are no valid reasons to do so
I strongly disagree. If a majority of people do not see the benefit of a
syntax change then the syntax should not be changed. A change to the
syntax of PHP has a ripple effect throughout the library and tools
ecosystem of PHP. This is a cost. This cost needs to be justified by a
benefit.
which costs do a new feature without BC break have for the ecosystem?
Again, obviously, the question remains, why would you care?
Really, that's what's obvious to you?
What's obvious to me is that if a method accepts two different types of parameters, you'd want to know which was passed in.
On 26 December 2017 18:35:29 GMT+00:00, "lists@rhsoft.net"
lists@rhsoft.net wrote:Am 26.12.2017 um 19:18 schrieb Larry Garfield:
If I may, I think the argument has always been that
- Foo & Bar makes total sense
- int|float makes total sense
- int & string is illogical so wouldn't matter anyway
not true
function x(int|string $x)
{$x = (int)$x;
}
I think there's a misunderstanding here. Some previous proposals for
"union
types" also included "intersection types", so that you could assert
"parameter must implement both of these interfaces". So 'Foo|Bar $x'
would
mean '$x instanceof Foo || $x instanceof Bar' and 'Foo&Bar $x' would
mean
'$x instanceof Foo && $x instanceof Bar'.I presume that's what Larry means by "int & string is illogical",
because it
would translate to "is_int($x) && is_string($x)", which is false for all
values of $x. This is different from "int | string", which, as you say,
might have valid uses.Regards,
Correct. Union types I've always seen presented as offering both union
and
intersection. There are cases where union is great, and where it's kinda
silly. There are cases where intersect is great, and where it's kinda
silly.Most of the anti- arguments I've seen for "union types" have fixated on
"int &&
string is meaningless, and Foo || Bar is bad design, so union types are
bad!"
Entirely ignoring the flip side, which is int || string (valid use
cases) and
Foo && Bar (many many valid use cases).--Larry Garfield
What is the use case for
int|float
? I mean, if f is able to process a
float
than f is able to process anint
and sinceint
is already
automatically changed to afloat
, well, you're done.The only situation (I can think of) where this might make a difference
is while formatting them. However, in those cases one usually wants to
accept many more types (or better yet, let the type format itself).I think that the union RFC was missing proper rules for disjunction (and
conjunction if intersection were to be included) as well as information
on disjointness. The latter would be important for exhaustive switches
that are enforced by the runtime (we'd probably need new keywords here,
e.g.match
+is
).
int|float is the natural type of numeric operations in PHP. Integers
automatically overflow into floating point numbers, so signatures using int
in conjunction with numeric operations are somewhat problematic.
Having an explicit number type also goes well with an explicit number cast.
PHP internally has a notion of a number cast (which is the basis for
arithmetic operations), but currently does not expose it. As such, number
casts currently have to be simulated using workarounds like +$x.
Regarding the union type RFCs, from what I remember, one of my personal
issues with it were the complex rules involving scalar type unions in weak
typing mode. It's non-trivial to decide what a value should be casted to if
it does not have the correct type. It's sort of clear what "1.5" passed to
an int|float union becomes, but it's not intuitively obvious what should
happen if you pass "foo" to a bool|int union, etc.
Regards,
Nikita
Am 29.12.2017 um 16:37 schrieb Nikita Popov:
Regarding the union type RFCs, from what I remember, one of my personal
issues with it were the complex rules involving scalar type unions in weak
typing mode. It's non-trivial to decide what a value should be casted to if
it does not have the correct type. It's sort of clear what "1.5" passed to
an int|float union becomes, but it's not intuitively obvious what should
happen if you pass "foo" to a bool|int union, etc.
in case of "bool|int" cast it to bool
in case of "int|bool" cast it to int
first comes, first serve
int|float is the natural type of numeric operations in PHP. Integers
automatically overflow into floating point numbers, so signatures using int
in conjunction with numeric operations are somewhat problematic.Having an explicit number type also goes well with an explicit number cast.
PHP internally has a notion of a number cast (which is the basis for
arithmetic operations), but currently does not expose it. As such, number
casts currently have to be simulated using workarounds like +$x.
Not sure I fully understand what you are saying.
I mean, the accuracy problem will prevail no matter what because at some
point we have to change that long to a double. The earlier the better so
users know what they are dealing with.
+$x
seems like something that is only of interest if my source is a
string and I don't know if it should be int or float. This on the other
hand sounds like something that is happening at the edges of the
application. Or maybe you had something else in mind?
Regarding the union type RFCs, from what I remember, one of my personal
issues with it were the complex rules involving scalar type unions in weak
typing mode. It's non-trivial to decide what a value should be casted to if
it does not have the correct type. It's sort of clear what "1.5" passed to
an int|float union becomes, but it's not intuitively obvious what should
happen if you pass "foo" to a bool|int union, etc.
Why exactly is it necessary to support weak mode together with unions
and intersections? It is obviously unclear in many situations what
should happen, so why not simply bail like in strict mode? I mean,
strict mode was added for backwards compatibility reasons. This is a new
future, there is no backwards compatibility. Anyone using it shall abide
to the strict rules of it.
--
Richard "Fleshgrinder" Fussenegger
Am 29.12.2017 um 18:13 schrieb Fleshgrinder:
Why exactly is it necessary to support weak mode together with unions
and intersections? It is obviously unclear in many situations what
should happen, so why not simply bail like in strict mode?
is also a good enough way to handle it now as we have the problem that a
type-hinted param don't accept NULL
and cast it to 0 anyways even in
weak mode
What is the use case for
int|float
? I mean, if f is able to process a
float
than f is able to process anint
and sinceint
is already
automatically changed to afloat
, well, you're done.int|float is the natural type of numeric operations in PHP. Integers
automatically overflow into floating point numbers, so signatures using int
in conjunction with numeric operations are somewhat problematic.
In my humble opinion, introducing int|float
or number
types would
not solve the real problem, namely that overflowing to float easily
causes unexpected behavior. For instance:
PHP_INT_MAX
+ 1 > PHP_INT_MAX
// => false (64bit arch)
--
Christoph M. Becker
Am 29.12.2017 um 16:37 schrieb Nikita Popov:
Having an explicit number type also goes well with an explicit number cast.
PHP internally has a notion of a number cast (which is the basis for
arithmetic operations), but currently does not expose it. As such, number
casts currently have to be simulated using workarounds like +$x.
PHP also already has is_numeric()
.
Am 26.12.2017 um 16:46 schrieb lists@rhsoft.net:
would you mind to explain this?
"Foo|Bar", "array|string", etc. (still) make no sense to me.
"scalar" makes sense to me although it is but an alias for
"bool|float|int|string".
Would you feel differently if we combined union types with type aliasing?
use Numeric = int | float;
function foo(Numeric $bar) { ... }
-Sara
Am 26.12.2017 um 20:15 schrieb Sara Golemon:
Am 26.12.2017 um 16:46 schrieb lists@rhsoft.net:
would you mind to explain this?
"Foo|Bar", "array|string", etc. (still) make no sense to me.
"scalar" makes sense to me although it is but an alias for
"bool|float|int|string".Would you feel differently if we combined union types with type aliasing?
use Numeric = int | float;
function foo(Numeric $bar) { ... }
looks also good for me (while directed to someone else) but i would
still prefer function foo(int|float $bar) because it's more native and
hence works finally the same way for every code which targets the
minimum PHP version where it's available
at the first look "use Numeric = int | float" looks gut, but you need
namespaces to avoid collisions and the same thing could have a different
meaning in different namespaces while "int|float" is very clear and
could also be "myClassA|myClassB" without explicit declaration
what could also be a problem with "use Numeric = int | float" is what
happens with existing code if someone changes that line by add another
type while functions don#t expect that one as input at the time they
where written - that can't happen with "int|float" in the function
declaration because it's a case-by-case decision
anyways, it would be a good progress if PHP programmers would be able to
declare type-hints for everything in hwatever way it is implemented
phpdoc even warns when documentation block and type hints differ and
it's something to enhance code quality compared to "i can't declare a
typehint at all or need 3 different functions"
Hi all,
Am 26.12.2017 um 16:46 schrieb lists@rhsoft.net:
would you mind to explain this?
"Foo|Bar", "array|string", etc. (still) make no sense to me.
"scalar" makes sense to me although it is but an alias for
"bool|float|int|string".
I followed the discussion and found it interesting how strong the focus
shifted about the discussion of the practical use cases. I would be for
union types or scalars in ab instant, but I never had reason to scan our
code-base and figure what would make really sense.
Mind you this is a private codebase (Laravel based, as can be seen by
some of the class names) but one where we tend to be very explicit about
documenting everything, thus I knew I could just go ahead and extract
the type hints using the '|' character and get back a pretty complete
picture:
$ grep -r '@param' *|grep | | awk '{ print $4 }'|sort -u
Attachment[]|Collection
Attachment[]|null
Builder|null
Channel|Post|int|null
Channel|int|null
ClientInterface|null
Client|int|null
Collection|Attachment[]
Collection|Channel[]
Collection|Comment[]
Collection|JsonapiModel[]
Collection|Model[]
Collection|Post[]
DateTime|Carbon|string|null
Dispatcher|null
EloquentCollection|JsonapiModel[]
Exception|null
Group|Group[]|Client|Client[]|Channel|Channel[]|null
Group|int|null
JsonapiModel|null
Model|int|null
Post[]|null
Post|int|null
Profile|int|null
ResponseInterface|null
Throwable|null
User|int|null
\ArrayAccess|array|JsonapiModel[]|null
array|false
array|null
int[]|int
int|int[]
int|null
mixed|null
null|LoggerInterface
null|Model
null|Model[]|Collection
null|string
string[]|Expression[]
string|int
string|null
This is extracted from a codebase with ~400 files and ~43k LOC (incl.
comments). I'm not saying anything like this is relevant codebase, but
it's a project whose code quality I know well and I know the reason for
every piece there.
Since the discussion focuses on scalars here and ignoring all the
nullables (they're auto-documented that way via PhpStorm) as well as
ignoring the pseudo array-type hints, this leaves us really with only:
- array|false
Only 1 occurrence, used for crafting a remote HTTP call which has an
interface which accepts either an array or false ?♀️ - string|int
Only 1 occurrence, used for error codes which may come in either
flavor, send back via HTTP somewhere
I was surprised how less of a use we would really have for a) scalar
itself and b) scalar unions.
OTOH, typed array hints seem to be very useful as are class based union
types. Although if one looks closely, it's clear this isn't a "union"
problem, but rather an emulation due to the absence of Generics, e.g.
Attachment[]|Collection
Is clearly a replacement for something like
Collection<Attachment>
I hope I didn't distract to much from the discussion but though I throw
in some data from an actual application. I'm sure there are much better
OSS projects out there which could be used for such analysis.
thanks,
- Markus
Am 29.12.2017 um 18:29 schrieb Markus Fischer:
Am 26.12.2017 um 16:46 schrieb lists@rhsoft.net:
would you mind to explain this?
"Foo|Bar", "array|string", etc. (still) make no sense to me.
"scalar" makes sense to me although it is but an alias for
"bool|float|int|string".I followed the discussion and found it interesting how strong the focus
shifted about the discussion of the practical use cases
because a lot of people demand that instead realize that with
union-types all that discussions would have ended because it's totally
on the application side written in PHP after that
Hey All.
Am 26.12.2017 um 14:38 schrieb Sebastian Bergmann sebastian@php.net:
Am 24.12.2017 um 15:34 schrieb Fleshgrinder:
I prepared a PR to add thescalar
pseudo-type to PHP after the
discussions around adding amixed
pseudo-type. I strongly believe that
it makes sense to provide the most common primitive union types with
handy aliases even if we are going to add union types in the future to PHP.Thank you, Richard, for working on this.
I spent a lot of time this year introducing scalar type declarations into
existing code bases. In quite a few cases I was not able to do so because
the existing code works with parameters that can be of two or more scalar
types. With PHP 7.2, I can only document this outside of the code using
@param annotations (until the code has been refactored to work with only
one parameter type).With a "scalar" type declaration I would not have to fall back to @param
annotations and could express the type in actual syntax.
Just a stupid question from someone that isn't following all the discussions here. So forgive me if the question was already asked and answered.
What would be wrong with a "composed type hint"? Something like int|float|double $numeric. Or array|Traversable $iterator?
It would allow people to add "mixed" typehints without having to introduce multiple typehints that combine different scalar typehints. And it could even allow typehinting different Objects in one function.
Yes, it allows people to shoot themselfes into their own feet.
And I'd only allow that for parameter types and never ever for return types.
But it might be an idea.
Cheers
Andreas
In some distant future it could be implemented in more natural way with
classes for primitive types and automatic boxing/unboxing for primitive
built-in types:
Php\Type\Object
Php\Type\Scalar
⌊ Php\Type\String
⌊ Php\Type\Integer
⌊ Php\Type\Float
⌊ Php\Type\Boolean
Php\Type\Array
And it will be easy to typehint all scalars via common parent class:
public function acceptsScalar(Scalar $scalarValue).
But all scalar objects in this case should be implemented as immutable or
should be passed as copy (like arrays). So, I would vote no for adding
special scalar typehint without mapping it to the common class or
interface, like iterable => Traversable, callable => Closure, etc
Best regards, Alexander
2017-12-26 18:59 GMT+03:00 Andreas Heigl andreas@heigl.org:
Hey All.
Am 26.12.2017 um 14:38 schrieb Sebastian Bergmann sebastian@php.net:
Am 24.12.2017 um 15:34 schrieb Fleshgrinder:
I prepared a PR to add thescalar
pseudo-type to PHP after the
discussions around adding amixed
pseudo-type. I strongly believe that
it makes sense to provide the most common primitive union types with
handy aliases even if we are going to add union types in the future to
PHP.Thank you, Richard, for working on this.
I spent a lot of time this year introducing scalar type declarations into
existing code bases. In quite a few cases I was not able to do so because
the existing code works with parameters that can be of two or more scalar
types. With PHP 7.2, I can only document this outside of the code using
@param annotations (until the code has been refactored to work with only
one parameter type).With a "scalar" type declaration I would not have to fall back to @param
annotations and could express the type in actual syntax.Just a stupid question from someone that isn't following all the
discussions here. So forgive me if the question was already asked and
answered.What would be wrong with a "composed type hint"? Something like
int|float|double $numeric. Or array|Traversable $iterator?It would allow people to add "mixed" typehints without having to introduce
multiple typehints that combine different scalar typehints. And it could
even allow typehinting different Objects in one function.Yes, it allows people to shoot themselfes into their own feet.
And I'd only allow that for parameter types and never ever for return
types.But it might be an idea.
Cheers
Andreas
In some distant future it could be implemented in more natural way with
classes for primitive types and automatic boxing/unboxing for primitive
built-in types:Php\Type\Object
Php\Type\Scalar
⌊ Php\Type\String
⌊ Php\Type\Integer
⌊ Php\Type\Float
⌊ Php\Type\Boolean
Php\Type\ArrayAnd it will be easy to typehint all scalars via common parent class:
public function acceptsScalar(Scalar $scalarValue).
But all scalar objects in this case should be implemented as immutable or
should be passed as copy (like arrays). So, I would vote no for adding
special scalar typehint without mapping it to the common class or
interface, like iterable => Traversable, callable => Closure, etcBest regards, Alexander
This is what I would like to see but making the primitives objects
without too much BC is a complicated thing to achieve. Auto-boxing could
be a solution. However, we would need many new things and new things are
in general not well received in the PHP world if they replace other things.
--
Richard "Fleshgrinder" Fussenegger