Variadic is_*() functions

php.internals

Enno Woortmann

7 years ago
Hi internals, as I reviewed a bunch of code for handling data from different sources (eg. json) in the last days I stumbled over code like this multiple times: if (!(is_numeric($input['example1']) && is_numeric($input['example2']))) { if (!is_numeric($input['example1'] || !is_numeric($input['example2'])) { and I had multiple problems with this. * it's kinda hard to read * multiple writings for the same logic * ends up in complex conditionals I searched for discussions regarding this topic and found it was mentioned in a 'side thread' of the RFC for changing empty() to a variadic a few years ago (https://externals.io/message/82549#82641) and I'd like to collect some feedback if it's wothy to revisit the topic to write the above example as: if (!is_numeric($input['example1'], $input['example2'])) { Except the is_callable() method all is_*() methods could be extended with a variadic behaviour which would check the given values from the left to the right and abort if a value doesn't match the condition (in the example if a given value is not numeric). So all in all there are some points to talk about: Revisit the discussion? Which functions are reasonable to be extended? If all functions are reasonable: what to do with is_callable()? regards, Enno

Levi Morrison

7 years ago
On Mon, Feb 11, 2019 at 8:39 AM Woortmann, Enno <enno.woortmann@web.de> wrote:
> > Hi internals, > > as I reviewed a bunch of code for handling data from different sources > (eg. json) in the last days I stumbled over code like this multiple times: > > > if (!(is_numeric($input['example1']) && is_numeric($input['example2']))) { > > > if (!is_numeric($input['example1'] || !is_numeric($input['example2'])) { > > > and I had multiple problems with this. > > * it's kinda hard to read > > * multiple writings for the same logic > > * ends up in complex conditionals > > > I searched for discussions regarding this topic and found it was > mentioned in a 'side thread' of the RFC for changing empty() to a > variadic a few years ago (https://externals.io/message/82549#82641) and > I'd like to collect some feedback if it's wothy to revisit the topic to > write the above example as: > > > if (!is_numeric($input['example1'], $input['example2'])) { > > > Except the is_callable() method all is_*() methods could be extended > with a variadic behaviour which would check the given values from the > left to the right and abort if a value doesn't match the condition (in > the example if a given value is not numeric). So all in all there are > some points to talk about: Revisit the discussion? Which functions are > reasonable to be extended? If all functions are reasonable: what to do > with is_callable()? > > regards, > Enno > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php
My position is the same: pushing the variadic behavior into the functions means that each function needs to pick `||` or `&&` behavior, both of which are useful. I would rather see more descriptive function names, such as `all_of` or `any_of`: if (!all_of('is_numeric', [$input['example1'], $input['example2']])) {/*...*/} These do not need to be part of PHP core, but perhaps they could be. I recognize that there is one downside, which is that lazy evaluation is lost, but generally don't see it to be an issue in these specific cases.

Chase Peeler

7 years ago
On Mon, Feb 11, 2019 at 10:59 AM Levi Morrison <levim@php.net> wrote:
> On Mon, Feb 11, 2019 at 8:39 AM Woortmann, Enno <enno.woortmann@web.de> > wrote: > > > > Hi internals, > > > > as I reviewed a bunch of code for handling data from different sources > > (eg. json) in the last days I stumbled over code like this multiple > times: > > > > > > if (!(is_numeric($input['example1']) && is_numeric($input['example2']))) > { > > > > > > if (!is_numeric($input['example1'] || !is_numeric($input['example2'])) { > > > > > > and I had multiple problems with this. > > > > * it's kinda hard to read > > > > * multiple writings for the same logic > > > > * ends up in complex conditionals > > > > > > I searched for discussions regarding this topic and found it was > > mentioned in a 'side thread' of the RFC for changing empty() to a > > variadic a few years ago (https://externals.io/message/82549#82641) and > > I'd like to collect some feedback if it's wothy to revisit the topic to > > write the above example as: > > > > > > if (!is_numeric($input['example1'], $input['example2'])) { > > > > > > Except the is_callable() method all is_*() methods could be extended > > with a variadic behaviour which would check the given values from the > > left to the right and abort if a value doesn't match the condition (in > > the example if a given value is not numeric). So all in all there are > > some points to talk about: Revisit the discussion? Which functions are > > reasonable to be extended? If all functions are reasonable: what to do > > with is_callable()? > > > > regards, > > Enno > > > > -- > > PHP Internals - PHP Runtime Development Mailing List > > To unsubscribe, visit: http://www.php.net/unsub.php > > My position is the same: pushing the variadic behavior into the > functions means that each function needs to pick `||` or `&&` > behavior, both of which are useful. I would rather see more > descriptive function names, such as `all_of` or `any_of`: > > if (!all_of('is_numeric', [$input['example1'], > $input['example2']])) {/*...*/} > > These do not need to be part of PHP core, but perhaps they could be. > > I recognize that there is one downside, which is that lazy evaluation > is lost, but generally don't see it to be an issue in these specific > cases. > > Lazy evaluation doesn't have to be lost if the all_of and any_of functions
are written correctly. all_of will return false as soon as one of them fails, and any_of will return true as soon as one of them passes. <?php function all_of($callback,...$params){ foreach($params as $param){ if(!$callback($param)){ return false; } } return true; } function any_of($callback,...$params){ foreach($params as $param){ if($callback($param)){ return true; } } return false; } Unless you are talking about cases like this: if(is_numeric(reallyFastFunc($foo)) || is_numeric(reallySlowFunc($bar))) In that case, you might be able to short circuit the evaluation of reallySlowFunc($bar), which wouldn't be the case with if(any_of('is_numeric',reallyFastFunc($foo),reallySlowFunc($bar))){} However, at that point, I'd say that 1.) If performance isn't an issue, break those function calls out and make your code more readable: $f1 = reallyFastFunc($foo); $f2 = reallySlowFunc($bar); if(any_of('is_numeric',$f1,$f2)){} 2.) If performance is an issue, fall back to the old method if(is_numeric(reallyFastFunc($foo)) || is_numeric(reallySlowFunc($bar))){}
> -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > --
-- Chase chasepeeler@gmail.com

Levi Morrison

7 years ago
>> I recognize that there is one downside, which is that lazy evaluation >> is lost, but generally don't see it to be an issue in these specific >> cases. >> > Lazy evaluation doesn't have to be lost if the all_of and any_of functions are written correctly. all_of will return false as soon as one of them fails, and any_of will return true as soon as one of them passes. > > Unless you are talking about cases like this: > if(is_numeric(reallyFastFunc($foo)) || is_numeric(reallySlowFunc($bar))) > In that case, you might be able to short circuit the evaluation of reallySlowFunc($bar), which wouldn't be the case with > if(any_of('is_numeric',reallyFastFunc($foo),reallySlowFunc($bar))){}
Yes, this is what I was referring to. As previously stated, I don't think it's likely to be an issue. If it is, then as you stated the normal boolean logic can be used in such places. ----- In other words, nothing needs to be done in PHP itself. Just write or use someone else's `all_of`, `any_of`, `none_of`, etc, functions.

Chase Peeler

7 years ago
On Mon, Feb 11, 2019 at 11:35 AM Levi Morrison <levim@php.net> wrote:
> >> I recognize that there is one downside, which is that lazy evaluation > >> is lost, but generally don't see it to be an issue in these specific > >> cases. > >> > > Lazy evaluation doesn't have to be lost if the all_of and any_of > functions are written correctly. all_of will return false as soon as one of > them fails, and any_of will return true as soon as one of them passes. > > > > Unless you are talking about cases like this: > > if(is_numeric(reallyFastFunc($foo)) || is_numeric(reallySlowFunc($bar))) > > In that case, you might be able to short circuit the evaluation of > reallySlowFunc($bar), which wouldn't be the case with > > if(any_of('is_numeric',reallyFastFunc($foo),reallySlowFunc($bar))){} > > Yes, this is what I was referring to. As previously stated, I don't > think it's likely to be an issue. If it is, then as you stated the > normal boolean logic can be used in such places. > > ----- > > In other words, nothing needs to be done in PHP itself. Just write or > use someone else's `all_of`, `any_of`, `none_of`, etc, functions. > > I'm sure there would be performance enhancement by having it handled with
C vs PHP. I would guess you'd probably need to implement separate functions for each is_* variant though: any_numeric, all_numeric, none_numeric, any_boolean, all_boolean, none_boolean, etc. You could probably still have the any_of, all_of, none_of versions as a shortcut though. I'm speaking from a point of some ignorance though in terms of what is and isn't faster when done in C. Whether it needs to be in core/bundled extension is another debate though. Seems like it might be a good thing for someone wanting to learn to write extensions to get their feet wet with, though. I did something similar when I wanted to try out writing an extension. I implemented a left($str,$len) and right($str,$len) method... which are basically just shortcuts for substr*.. but gave me some experience with all of the boiler plate needed, as well as utilizing existing functions. *left($str,$len) => substr($str,0,$len) right($str,$len) => substr($str,-$len);
> -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > --
-- Chase chasepeeler@gmail.com

Christoph Becker

7 years ago
On 11.02.2019 at 16:59, Levi Morrison wrote:
> My position is the same: pushing the variadic behavior into the > functions means that each function needs to pick `||` or `&&` > behavior, both of which are useful. I would rather see more > descriptive function names, such as `all_of` or `any_of`: > > if (!all_of('is_numeric', [$input['example1'], > $input['example2']])) {/*...*/} > > These do not need to be part of PHP core, but perhaps they could be. > > I recognize that there is one downside, which is that lazy evaluation > is lost, but generally don't see it to be an issue in these specific > cases.
See <https://github.com/php/php-src/pull/1385>, which has been abanoned, though.
-- Christoph M. Becker

Sara Golemon

7 years ago
On Mon, Feb 11, 2019 at 9:59 AM Levi Morrison <levim@php.net> wrote:
> My position is the same: pushing the variadic behavior into the > functions means that each function needs to pick `||` or `&&` > behavior, both of which are useful. >
Additionally, I recall from that thread (or a similar one on the same topic) some pushback on isset() even having behavior like this. That it should be considered a mistake. Not saying I agree with going so far as to call it a mistake, but it does seem to have been an less than ideal choice.
> I would rather see more > descriptive function names, such as `all_of` or `any_of`: >
The feels like your C++ experience talking. :D I do agree though, I think this model gives much more power and flexibility, I'd also favor such functions taking iterables (as you show in your examples) over variadic signatures (as shown in some replies) because this allows the short-circuiting to halt generators early as well.
> These do not need to be part of PHP core, but perhaps they could be. >
My preference is for the community to build what they want in userspace, then worry about teaching the engine to optimize the pattern when it sees it. -Sara

Côme Chilliet

7 years ago
Le lundi 11 février 2019, 08:59:17 CET Levi Morrison a écrit :
> My position is the same: pushing the variadic behavior into the > functions means that each function needs to pick `||` or `&&` > behavior, both of which are useful. I would rather see more > descriptive function names, such as `all_of` or `any_of`: > > if (!all_of('is_numeric', [$input['example1'], > $input['example2']])) {/*...*/}
My first thought was that this must already exists under another name, but it seems that none of array_map, array_walk or array_reduce allows to do that easily. (That is, in a more readable way than the boolean operator string, otherwise it’s useless) Côme

Sara Golemon

7 years ago
On Tue, Feb 12, 2019 at 4:30 AM Côme Chilliet <come@opensides.be> wrote:
> My first thought was that this must already exists under another name, > but it seems that none of array_map, array_walk or array_reduce allows to
do that easily.
> (That is, in a more readable way than the boolean operator string,
otherwise it’s useless)
>
Not in core, but I and I'm sure others, have built that pattern more than once. Ref: https://github.com/phplang/generator/blob/master/src/iterable.php -Sara