# Allowing is_* functions to accept multiple parameters

- Thread: https://externals.io/message/69180
- Original archive: https://news-web.php.net/php.internals/69180
- Started by Leigh on 2013-09-18T09:53:40+00:00
- Messages: 19
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #69180 — Leigh — 2013-09-18T09:53:40+00:00

URL: https://externals.io/message/69180#69180

Hi Internals.

How do you feel about expanding the is_* functions to accept multiple
parameters similar to the way isset() already does?

From the manual: "If multiple parameters are supplied then isset() will
return `TRUE` only if all of the parameters are set. Evaluation goes from
left to right and stops as soon as an unset variable is encountered."

I am proposing that all of the is_* functions are allowed to take a
variable number of parameters and will return true if all of the arguments
satisfy what is being checked.

i.e. is_null($a, $b, $c) would be the same as  is_null($a) && is_null($b)
&& is_null($c)

As well as producing much more succinct code and less function call
overhead, I think this ties in nicely with the variadic and unpacking RFCs
and is especially useful for parameters that cannot be typehinted; for
example you would be able to something like:

function doStuffWithNumbers(...$numbers) {
    if (!is_int(...$numbers)) {
        throw new InvalidArgumentException('blah');
...

Thoughts?

---

## #69181 — Vartolomei Nicolae — 2013-09-18T09:55:43+00:00

URL: https://externals.io/message/69180#69181 · In reply to: #69180

> Hi Internals.
> 
> How do you feel about expanding the is_* functions to accept multiple
> parameters similar to the way isset() already does?
> 
> From the manual: "If multiple parameters are supplied then isset() will
> return `TRUE` only if all of the parameters are set. Evaluation goes from
> left to right and stops as soon as an unset variable is encountered."
> 
> I am proposing that all of the is_* functions are allowed to take a
> variable number of parameters and will return true if all of the arguments
> satisfy what is being checked.
> 
> i.e. is_null($a, $b, $c) would be the same as is_null($a) && is_null($b)
> && is_null($c)
> 
> As well as producing much more succinct code and less function call
> overhead, I think this ties in nicely with the variadic and unpacking RFCs
> and is especially useful for parameters that cannot be typehinted; for
> example you would be able to something like:
> 
> function doStuffWithNumbers(...$numbers) {
> if (!is_int(...$numbers)) {
> throw new InvalidArgumentException('blah');
> ...
> 
> Thoughts? 
are_int() 

kindly,
nvartolomei



=)

---

## #69183 — Igor Wiedler — 2013-09-18T11:15:20+00:00

URL: https://externals.io/message/69180#69183 · In reply to: #69180

> Hi Internals.
> 
> How do you feel about expanding the is_* functions to accept multiple
> parameters similar to the way isset() already does?
> 
> ...
> 
> Thoughts?

For isset() there is a good reason to do this, because the var might not exist at all, and as such you cannot use a function, as it would produce warnings.

However, for is_* functions, this could easily be done with a higher-order "every" function. You pass a predicate and an array of values. It returns a boolean.

Example:

	if (!every('is_int', $numbers)) {
		throw new \InvalidArgumentException(...);
	}

Not only is that much cleaner in my opinion, it also is composable without having to change *all* of the is_* predicates.

As such, I don't see much value in changing this.

Igor

---

## #69185 — Sebastian Krebs — 2013-09-18T12:14:50+00:00

URL: https://externals.io/message/69180#69185 · In reply to: #69183

2013/9/18 Igor Wiedler <igor@wiedler.ch>

> 
>
> > […]
>
> For isset() there is a good reason to do this, because the var might not
> exist at all, and as such you cannot use a function, as it would produce
> warnings.
>
> However, for is_* functions, this could easily be done with a higher-order
> "every" function. You pass a predicate and an array of values. It returns a
> boolean.
>
> Example:
>
>         if (!every('is_int', $numbers)) {
>                 throw new \InvalidArgumentException(...);
>         }
>

Actually it was about multiple parameters

    if (!every('is_int', $a, $b, $c)) { /* .. */ }

To me using `is_int()` directly feels more intuitive too.

    if (!is_int($a, $b, $c)) { /* .. */ }



>
> Not only is that much cleaner in my opinion, it also is composable without
> having to change *all* of the is_* predicates.
>
> As such, I don't see much value in changing this.
>

Well, asking me that sounds like the same value like it is for
 `array_column()`. It is a minor improvement, but it is an useful one (imo).

Just my 2 cent


Regards,
Sebastian


> Igor
>
>
> --
> 
> 
>
>


-- 
github.com/KingCrunch

---

## #69186 — Frank Liepert — 2013-09-18T12:21:08+00:00

URL: https://externals.io/message/69180#69186 · In reply to: #69180

> function doStuffWithNumbers(...$numbers) {
>     if (!is_int(...$numbers)) {
>         throw new InvalidArgumentException('blah');
> ...
> 
> Thoughts?

Returning bool in this case will make it impossible to respond with a
meaningful error message. Which of the provided arguments !is_int()?

Instead, I would go with f.Ex.:

$numbers = array(...$numbers);
$result = array_filter($numbers, "is_int")

and compare the arrays. Since the array keys are preserved, it's easy to
find out, which argument !is_int().


Finally, the imho best solution would still be introducing optional scalar
type hinting. But that is another topic and shouldn't be discussed here.

---

## #69189 — Leigh — 2013-09-18T12:57:42+00:00

URL: https://externals.io/message/69180#69189

>
> for is_* functions, this could easily be done with a higher-order "every" function. You pass a predicate and an array of values. It returns a boolean.
>
> Example:
>
>         if (!every('is_int', $numbers)) {
>                 throw new \InvalidArgumentException(...);
>         }
>
> Not only is that much cleaner in my opinion, it also is composable without having to change *all* of the is_* predicates.

Might just be me, but I don't really like having function names as
strings (obviously I use it when I _have_ to, but I try and stay away
from it otherwise).

Feedback I'm really interested in is what issues could arise from
changing those predicates. I'm pretty short sighted in this regard,
and mainly see the benefits I would gain from this change, so looking
for more "This is a terrible idea because..." kind of stuff.



>
> Returning bool in this case will make it impossible to respond with a
> meaningful error message. Which of the provided arguments !is_int()?

I agree that it doesn't fit every case, and it's not supposed to. The
same can be said of isset(), if it is important to know which argument
was not set, then you have to use multiple calls.



>
> Finally, the imho best solution would still be introducing optional scalar
> type hinting. But that is another topic and shouldn't be discussed here.

I agree, (both that it would be better, and that it is another topic
entirely - one which usually goes in circles, so lets stay away from
it here :))

---

## #69191 — Sean Coates — 2013-09-18T13:50:30+00:00

URL: https://externals.io/message/69180#69191 · In reply to: #69181

> i.e. is_null($a, $b, $c) would be the same as is_null($a) && is_null($b)
> && is_null($c)

Note that this would not be semantically equivalent in this form, even if `is_null()` did accept multiple parameters, because of the short-circuiting with `&&`:

<?php

function are_null() {
	foreach (func_get_args() as $a) {
		if ($a !== null) {
			return false;
		}
	}
	return true;
}

function destroy_data() { echo "DESTROYING DATA\n"; }

// old form: short-circuited; data is not destroyed
if (is_null(null) && is_null(false) && is_null(destroy_data())) {
	echo "All null.\n";
} else {
	echo "Not null.\n";
}

echo "----\n";

// proposed form: no short-circuit; parameters are evaluated at call time and data is destroyed
if (are_null(null, false, destroy_data())) {
	echo "Still null.\n";
} else {
	echo "Still not null.\n";
}

---

## #69192 — Chris London — 2013-09-18T13:57:44+00:00

URL: https://externals.io/message/69180#69192 · In reply to: #69191

I like the naming convention of are_*.  For me personally it isn't directly
intuitive that the multiple parameters of is_* would be compared with an &&
and not an ||.




> > […]
>
> Note that this would not be semantically equivalent in this form, even if
> `is_null()` did accept multiple parameters, because of the short-circuiting
> with `&&`:
>
> <?php
>
> function are_null() {
>         foreach (func_get_args() as $a) {
>                 if ($a !== null) {
>                         return false;
>                 }
>         }
>         return true;
> }
>
> function destroy_data() { echo "DESTROYING DATA\n"; }
>
> // old form: short-circuited; data is not destroyed
> if (is_null(null) && is_null(false) && is_null(destroy_data())) {
>         echo "All null.\n";
> } else {
>         echo "Not null.\n";
> }
>
> echo "----\n";
>
> // proposed form: no short-circuit; parameters are evaluated at call time
> and data is destroyed
> if (are_null(null, false, destroy_data())) {
>         echo "Still null.\n";
> } else {
>         echo "Still not null.\n";
> }

---

## #69198 — Patrick ALLAERT — 2013-09-18T14:48:49+00:00

URL: https://externals.io/message/69180#69198 · In reply to: #69191

2013/9/18 Sean Coates <sean@seancoates.com>:
> > […]
>
> Note that this would not be semantically equivalent in this form, even if `is_null()` did accept multiple parameters, because of the short-circuiting with `&&`:
>
> <?php
>
> function are_null() {
>         foreach (func_get_args() as $a) {
>                 if ($a !== null) {
>                         return false;
>                 }
>         }
>         return true;
> }
>
> function destroy_data() { echo "DESTROYING DATA\n"; }
>
> // old form: short-circuited; data is not destroyed
> if (is_null(null) && is_null(false) && is_null(destroy_data())) {
>         echo "All null.\n";
> } else {
>         echo "Not null.\n";
> }
>
> echo "----\n";
>
> // proposed form: no short-circuit; parameters are evaluated at call time and data is destroyed
> if (are_null(null, false, destroy_data())) {
>         echo "Still null.\n";
> } else {
>         echo "Still not null.\n";
> }
>

Not a good idea IMHO: it would complexify the execution a lot, think about:

$test = "are_null";

if ($test(null, false, destroy_data())) {
        echo "Still null.\n";
} else {
        echo "Still not null.\n";
}

Looking at the AST wouldn't be enough to tell what would or wouldn't
be short-circuited.

It looks fine to have is_* functions working on multiple values as
Leigh originally proposed. I also feel that it would mostly be
used/useful on variables.
People worrying about performance and/or execution of code with
short-circuiting can (and should) still rely on "&&".

---

## #69199 — Sean Coates — 2013-09-18T14:52:25+00:00

URL: https://externals.io/message/69180#69199 · In reply to: #69198

> Not a good idea IMHO: it would complexify the execution a lot, think about:

To be clear, I wasn't proposing an alternative. I was just pointing out that function call semantics are not the same as conditional (short-circuit) semantics, as they appeared in the OP.

S

---

## #69200 — Patrick ALLAERT — 2013-09-18T14:53:18+00:00

URL: https://externals.io/message/69180#69200 · In reply to: #69192

2013/9/18 Chris London <me@chrislondon.co>:
> I like the naming convention of are_*.  For me personally it isn't directly
> intuitive that the multiple parameters of is_* would be compared with an &&
> and not an ||.

isset() already operates that way, keeping "is_" and implementing it
as originally proposed by Leigh would, at least, be consistent.

[Derick mode: on]
PS: top posting is not #cool.

---

## #69201 — Patrick ALLAERT — 2013-09-18T14:54:53+00:00

URL: https://externals.io/message/69180#69201 · In reply to: #69199

2013/9/18 Sean Coates <sean@seancoates.com>:
> > […]
>
> To be clear, I wasn't proposing an alternative. I was just pointing out that function call semantics are not the same as conditional (short-circuit) semantics, as they appeared in the OP.

That is true! Something to take in mind for the documentation if this
is going to be implemented!

---

## #69204 — Leigh — 2013-09-18T16:09:06+00:00

URL: https://externals.io/message/69180#69204 · In reply to: #69200

> i.e. is_null($a, $b, $c) would be the same as is_null($a) && is_null($b)
> && is_null($c)
>
> Note that this would not be semantically equivalent in this form, even if
> `is_null()` did accept multiple parameters, because of the short-circuiting
> with `&&`:

See below.


> 2013/9/18 Chris London <me@chrislondon.co>:
> > […]
>
> isset() already operates that way, keeping "is_" and implementing it
> as originally proposed by Leigh would, at least, be consistent.

Indeed, my proposal was to mimic short circuiting as isset() does it,
evaluating LTR and returning false at the earliest opportunity.

---

## #69205 — Bob Weinand — 2013-09-18T17:50:36+00:00

URL: https://externals.io/message/69180#69205 · In reply to: #69204

Am 18.9.2013 um 18:09 schrieb Leigh <leight@gmail.com>:
> 
> > […]
> 
> See below.
> 
> 
> > […]
> 
> Indeed, my proposal was to mimic short circuiting as isset() does it,
> evaluating LTR and returning false at the earliest opportunity.

At least, from a technical point, evaluating LTR would require to change the engine
(would be some more complex change as it would require to switch between contexts
and being able to execute the ZEND_SEND_VAL opcodes one by one (evaluate
first argument and execute until next ZEND_SEND_VAL, and if one doesn't match the
conditions, jump to the end of the sending args) etc.) Just have a look at the complicated
opcodes, just for isset: http://3v4l.org/l3Z4l/vld And now do something like this for real
functions (e.g. not a language construct) at run-time, because we'd have to do the
function lookup at run-time (just because the name of the function might be unknown at
compile time thanks to $var = "func"; $var();)

The other alternative here is (like isset), just changing it into a language construct.
(which I consider also suboptimal, as it disallows still things like
$var = "is_*"; $var($arg1, $arg2, $arg3);).


So, I am basically -1 on this, as this a) requires some deep engine change in the
opcodes' handling flow or needs a change of language parser (well, if someone could
provide a nice patch which implements this in a nice way, I'm in favor of this) and
b) without LTR it effectively needs to evaluate everything, what would be
a performance drop as opposed to the current (encouraged) method of just and'ing all
the is_* calls. Then you also just could do in userspace code (with an one-liner):
function are_int (...$args) {
    return count($args) == count(array_filter("is_int", $args));
}

So: tl;dr: only in favour if technically not too complicated and doesn't have the restrictions of
a typical language construct. Else: -1. 

Bob Weinand

---

## #69214 — Leigh — 2013-09-18T19:57:27+00:00

URL: https://externals.io/message/69180#69214 · In reply to: #69205

> At least, from a technical point, evaluating LTR would require to change the engine
> (would be some more complex change as it would require to switch between contexts
> and being able to execute the ZEND_SEND_VAL opcodes one by one

Feels like you're overthinking it?

Plan was to allow any is_* function to be called as normal (SEND_VAR,
SEND_VAR ... FCALL), then iterate over the supplied arguments inside
the function and return false at the first failure, emulating the
boolean short circuit.

---

## #69215 — Bob Weinand — 2013-09-18T20:05:21+00:00

URL: https://externals.io/message/69180#69215 · In reply to: #69214

Am 18.09.2013 um 21:57 schrieb "Leigh" <leight@gmail.com>:
> 
> > […]
> 
> Feels like you're overthinking it?
> 
> Plan was to allow any is_* function to be called as normal (SEND_VAR,
> SEND_VAR ... FCALL), then iterate over the supplied arguments inside
> the function and return false at the first failure, emulating the
> boolean short circuit.

Well, the arguments are executed at the moment where the ZEND_SEND_VAR op is encountered. But this you already can do with the one liner in my last mail.

So LTR support is basically not worth it here (too many change/too much cost for this).

Bob Weinand

---

## #69220 — William Bartlett — 2013-09-19T02:20:00+00:00

URL: https://externals.io/message/69180#69220 · In reply to: #69215

I would argue that LTR support is also inconsistent / not desired.

If I wrote:

$i = 0;
is_three($i = $i + 1, $i = $i + 1, $i = $i + 1);

I would certainly expect is_three to return false, but I would also expect
$i to contain three.  php doesn't normally evaluate arguments lazily, it
would be weird for that behavior to suddenly crop up.  users who want lazy
evaluation can write it the traditional way (with &&).

PS - hi, this is my first post on php-internals though i've been reading
for like a month.

-Will Bartett

--
William Bartlett
College of Engineering | Cornell University '14
240-432-5189




> Am 18.09.2013 um 21:57 schrieb "Leigh" <leight@gmail.com>:
> > […]
> change the engine
> > […]
> between contexts
> > […]
>
> Well, the arguments are executed at the moment where the ZEND_SEND_VAR op
> is encountered. But this you already can do with the one liner in my last
> mail.
>
> So LTR support is basically not worth it here (too many change/too much
> cost for this).
>
> Bob Weinand

---

## #69228 — Leigh — 2013-09-19T09:10:23+00:00

URL: https://externals.io/message/69180#69228 · In reply to: #69220

On 19 September 2013 03:20, William Bartlett
<william.a.bartlett@gmail.com> wrote:
> I would argue that LTR support is also inconsistent / not desired.
>
> If I wrote:
>
> $i = 0;
> is_three($i = $i + 1, $i = $i + 1, $i = $i + 1);
>
> I would certainly expect is_three to return false, but I would also expect
> $i to contain three.  php doesn't normally evaluate arguments lazily, it
> would be weird for that behavior to suddenly crop up.  users who want lazy
> evaluation can write it the traditional way (with &&).

I think there has been some misunderstanding of my intention here
(maybe I communicated it badly) - Originally I was pretty confused
when reading Bobs response as it was way beyond the scope of what I
was proposing.

When I say parameters evaluated LTR / boolean short-curcuit evaluation
I mean it like this:

$i = 1;
$f = 1.1;

is_int($i, $f, $i, $i) => is_int(1) && is_int(1.1) && is_int(1) && is_int(1)

is_int($i++, $f++, $i++, $i++) =>  is_int(1) && is_int(1.1) &&
is_int(2) && is_int(3)

$i == 4;
$f == 2.1;

Internally, processing will stop at the is_int(1.1) and not bother
continuing to check the types of further arguments.

I did not mean:

is_int($i++, $f++, $i++, $i++) => is_int($i++) && is_int($f++) &&
is_int($i++) && is_int($i++)

As Bob said, this would take some pretty nuts opcode processing, and
is completely not worth the effort involved. I may have emphasised a
parallel with isset() a bit too much, however isset() cannot take
expressions as input.

I was never intending to try and evaluate parameters as they were
passed and jump over subsequent evaluations. Standard function call
semantics would still apply. I hope that people find that less
confusing / unexpected.

---

## #69231 — Bob Weinand — 2013-09-19T10:48:41+00:00

URL: https://externals.io/message/69180#69231 · In reply to: #69228

> Am 19.09.2013 um 11:10 schrieb "Leigh" <leight@gmail.com>:
> 
> On 19 September 2013 03:20, William Bartlett
> <william.a.bartlett@gmail.com> wrote:
> > […]
> 
> I think there has been some misunderstanding of my intention here
> (maybe I communicated it badly) - Originally I was pretty confused
> when reading Bobs response as it was way beyond the scope of what I
> was proposing.
> 
> When I say parameters evaluated LTR / boolean short-curcuit evaluation
> I mean it like this:
> 
> $i = 1;
> $f = 1.1;
> 
> is_int($i, $f, $i, $i) => is_int(1) && is_int(1.1) && is_int(1) && is_int(1)
> 
> is_int($i++, $f++, $i++, $i++) =>  is_int(1) && is_int(1.1) &&
> is_int(2) && is_int(3)
> 
> $i == 4;
> $f == 2.1;
> 
> Internally, processing will stop at the is_int(1.1) and not bother
> continuing to check the types of further arguments.
> 
> I did not mean:
> 
> is_int($i++, $f++, $i++, $i++) => is_int($i++) && is_int($f++) &&
> is_int($i++) && is_int($i++)
> 
> As Bob said, this would take some pretty nuts opcode processing, and
> is completely not worth the effort involved. I may have emphasised a
> parallel with isset() a bit too much, however isset() cannot take
> expressions as input.
> 
> I was never intending to try and evaluate parameters as they were
> passed and jump over subsequent evaluations. Standard function call
> semantics would still apply. I hope that people find that less
> confusing / unexpected.

Well, then I misunderstood what you meant.

Then the idea is acceptable. Not sure if we need this as it is very easily possible with an one-liner in userspace code.

I'm +- 0 on this.

Bob Weinand
