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?
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
returnTRUE
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
=)
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";
}
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 ||.
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";
}
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.
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:
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.
Indeed, my proposal was to mimic short circuiting as isset() does it,
evaluating LTR and returning false at the earliest opportunity.
Am 18.9.2013 um 18:09 schrieb Leigh leight@gmail.com:
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:
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.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
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.
Am 18.09.2013 um 21:57 schrieb "Leigh" leight@gmail.com:
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 oneFeels 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
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:
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 oneFeels 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
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.
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 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.
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
2013/9/18 Sean Coates sean@seancoates.com:
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";
}
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 "&&".
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
2013/9/18 Sean Coates sean@seancoates.com:
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.
That is true! Something to take in mind for the documentation if this
is going to be implemented!
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
>
>
> > 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(...);
> }
>
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
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.