Hi internals,
The primitives any() and all() are a common part of many programming languages and help in avoiding verbosity or unnecessary abstractions.
- https://hackage.haskell.org/package/base-4.14.0.0/docs/Prelude.html#v:any
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
- https://docs.python.org/3/library/functions.html#all
- https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#allMatch-java.util.function.Predicate-
For example, the following code could be shortened significantly
// Old version
$satisifes_predicate = false;
foreach ($items as $item) {
if (API::satisfiesCondition($item)) {
$satisfies_predicate = true;
break;
}
}
if (!$satisfies_predicate) {
throw new APIException("No matches found");
}
// New version is much shorter and readable
if (!any($items, fn($x) => API::satisfiesCondition($x))) {
throw new APIException("No matches found");
}
That example doesn't have any suitable helpers already in the standard library.
Using array_filter would unnecessarily call satisfiesCondition even after the first item was found,
and array_search doesn't take a callback.
A proposed implementation is https://github.com/php/php-src/pull/6053 - it takes similar flags and param orders to array_filter()
.
Previous discussion was in https://externals.io/message/103357#103373
- New contributors to projects wouldn't know about any() and all() if it was reimplemented with different semantics and only occasionally used
(e.g. MyArrayUtil::any()) in various projects) - If this was provided only in userland, there'd be low adoption and code such as the first example would remain common.
If the standard library provided it, then polyfills would as well, making cleaner code easier to write.
Thanks,
- Tyson
I like it!
What is the $use_flags parameter for?
On Sat, Aug 29, 2020 at 10:24 PM tyson andre tysonandre775@hotmail.com
wrote:
Hi internals,
The primitives any() and all() are a common part of many programming
languages and help in avoiding verbosity or unnecessary abstractions.
https://hackage.haskell.org/package/base-4.14.0.0/docs/Prelude.html#v:any
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
For example, the following code could be shortened significantly
// Old version $satisifes_predicate = false; foreach ($items as $item) { if (API::satisfiesCondition($item)) { $satisfies_predicate = true; break; } } if (!$satisfies_predicate) { throw new APIException("No matches found"); } // New version is much shorter and readable if (!any($items, fn($x) => API::satisfiesCondition($x))) { throw new APIException("No matches found"); }
That example doesn't have any suitable helpers already in the standard
library.
Using array_filter would unnecessarily call satisfiesCondition even after
the first item was found,
and array_search doesn't take a callback.A proposed implementation is https://github.com/php/php-src/pull/6053 -
it takes similar flags and param orders toarray_filter()
.Previous discussion was in https://externals.io/message/103357#103373
- New contributors to projects wouldn't know about any() and all() if it
was reimplemented with different semantics and only occasionally used
(e.g. MyArrayUtil::any()) in various projects)- If this was provided only in userland, there'd be low adoption and code
such as the first example would remain common.
If the standard library provided it, then polyfills would as well,
making cleaner code easier to write.Thanks,
- Tyson
--To unsubscribe, visit: https://www.php.net/unsub.php
Hi Alex,
I like it!
What is the $use_flags parameter for?
It's for deciding what parameters to pass to callback, like https://www.php.net/array_filter - the reflection name should probably just be $flag
Occasionally, the key might be useful to check against, e.g. the identifier of an item in a map, or SplObjectStorage.
- the default is
any($iterable, fn ($value) => ...)
- or
any($iterable, fn ($key) => expr, ARRAY_FILTER_USE_KEY)
- or
any($iterable, fn ($value, $key) => expr, ARRAY_FILTER_USE_BOTH)
Thanks,
- Tyson
Hi Alex,
I like it!
What is the $use_flags parameter for?
It's for deciding what parameters to pass to callback, like https://www.php.net/array_filter - the reflection name should probably just be $flag
Occasionally, the key might be useful to check against, e.g. the identifier of an item in a map, or SplObjectStorage.
- the default is
any($iterable, fn ($value) => ...)
- or
any($iterable, fn ($key) => expr, ARRAY_FILTER_USE_KEY)
- or
any($iterable, fn ($value, $key) => expr, ARRAY_FILTER_USE_BOTH)
Doesn't the occasional required use of long constant names like ARRAY_FILTER_USE_KEY
and ARRAY_FILTER_USE_BOTH
somewhat negative the benefit of a more concise syntax?
I know that many existing functions in PHP have a collection of long constant names that can be passed, but is that a style of programming that PHP really wants to continue to proliferate, or would it not be better to address that use-case in a different manner? (Admittedly doing so might require having this feature dependent on other critical path language features.)
One approach might be if PHP could allow for skipped parameters in callables — where I am using a single underscore to indicate an unused parameter (GoLang has a "blank identifier" for similar purposes so there is some prior art to consider) — e.g:
- the default is
any($iterable, fn ( $value ) => ...)
- or
any($iterable, fn ( _, $key ) => expr )
- or
any($iterable, fn ( $value, $key ) => expr )
PHP could handle it more generically, is possible, or recognize this and branch to different implementations of any() and all() if the goal is to optimize performance by only passing one value in key-only or value-only calls.
Optionally we could use null instead:
- the default is
any($iterable, fn ($value) => ...)
- or
any($iterable, fn (null, $key) => expr )
- or
any($iterable, fn ($value, $key) => expr )
-Mike
[1] https://www.geeksforgeeks.org/what-is-blank-identifierunderscore-in-golang/#:~:text=(underscore)%20in%20Golang%20is,Blank%20Identifier. <https://www.geeksforgeeks.org/what-is-blank-identifierunderscore-in-golang/#:~:text=(underscore)%20in%20Golang%20is,Blank%20Identifier.
Hi Mike Schinkel,
Doesn't the occasional required use of long constant names like
ARRAY_FILTER_USE_KEY
andARRAY_FILTER_USE_BOTH
somewhat negative the benefit of a more concise syntax?I know that many existing functions in PHP have a collection of long constant names that can be passed, but is that a style of programming that PHP really wants to continue to proliferate, or would it not be better to address that use-case in a different manner? (Admittedly doing so might require having this feature dependent on other critical path language features.)
One approach might be if PHP could allow for skipped parameters in callables — where I am using a single underscore to indicate an unused parameter (GoLang has a "blank identifier" for similar purposes so there is some prior art to consider) — e.g:
- the default is
any($iterable, fn ( $value ) => ...)
- or
any($iterable, fn ( _, $key ) => expr )
- or
any($iterable, fn ( $value, $key ) => expr )
PHP could handle it more generically, is possible, or recognize this and branch to different implementations of any() and all() if the goal is to optimize performance by only passing one value in key-only or value-only calls.
Optionally we could use null instead:
- the default is
any($iterable, fn ($value) => ...)
- or
any($iterable, fn (null, $key) => expr )
- or
any($iterable, fn ($value, $key) => expr )
Supporting "blank identifiers" is out of the scope of this RFC,
and would have some implementation concerns of how it'd work with named parameters
and how backtrace generation would be affected. I don't think there'd be a significant performance improvement over not using parameters.
I'm taking $flag out of the rfc because there's many proposed alternatives for doing this,
and checking values of an iterable is the most common use case.
Repeating the response to Marco Pivetta,
I find that checking parameter counts would be inconsistent with how other internal functions accepting callbacks such as array_filter
already behave.
It would also be unexpected if users or tooling wrap callbacks with other callbacks.
Developers would also expect func_get_args()
to work.
<?php
function my_none(iterable $values, callable $predicate) {
return all($values, fn(...$args) => $predicate(...$args));
}
// PHP can't distinguish between these when given `fn(...$args)`
// - would throw if passed 1 parameter
my_none($values, fn($value, $key) => $value == true || $key == 'x');
// - would throw if passed 2 parameters (too many)
my_none($values, 'is_string');
Cheers,
- Tyson
Hi Mike Schinkel,
Doesn't the occasional required use of long constant names like
ARRAY_FILTER_USE_KEY
andARRAY_FILTER_USE_BOTH
somewhat negative the benefit of a more concise syntax?I know that many existing functions in PHP have a collection of long constant names that can be passed, but is that a style of programming that PHP really wants to continue to proliferate, or would it not be better to address that use-case in a different manner? (Admittedly doing so might require having this feature dependent on other critical path language features.)
One approach might be if PHP could allow for skipped parameters in callables — where I am using a single underscore to indicate an unused parameter (GoLang has a "blank identifier" for similar purposes so there is some prior art to consider) — e.g:
- the default is
any($iterable, fn ( $value ) => ...)
- or
any($iterable, fn ( _, $key ) => expr )
- or
any($iterable, fn ( $value, $key ) => expr )
PHP could handle it more generically, is possible, or recognize this and branch to different implementations of any() and all() if the goal is to optimize performance by only passing one value in key-only or value-only calls.
Optionally we could use null instead:
- the default is
any($iterable, fn ($value) => ...)
- or
any($iterable, fn (null, $key) => expr )
- or
any($iterable, fn ($value, $key) => expr )
Supporting "blank identifiers" is out of the scope of this RFC,
and would have some implementation concerns of how it'd work with named parameters
and how backtrace generation would be affected. I don't think there'd be a significant performance improvement over not using parameters.I'm taking $flag out of the rfc because there's many proposed alternatives for doing this,
and checking values of an iterable is the most common use case.
Acknowledged.
If not a significant performance improvement why not pass both value and key to the callable in your RFC in case someone needs access to the key?
Adding the key as a second parameter would not preclude passing a flag to modify behavior in a future version of PHP if that is the internals consensus.
-Mike
P.S. John Bafford recently suggested the use of void in foreach loops for essentially the same reason as I proposed with underscore or null (and frankly void is better than underscore or null):
https://externals.io/message/111774 https://externals.io/message/111774
If his RFC were to pass then that would open the door to using it here, right?
Hey Tyson,
http://ocramius.github.com/
On Sat, Aug 29, 2020 at 10:24 PM tyson andre tysonandre775@hotmail.com
wrote:
Hi internals,
The primitives any() and all() are a common part of many programming
languages and help in avoiding verbosity or unnecessary abstractions.
https://hackage.haskell.org/package/base-4.14.0.0/docs/Prelude.html#v:any
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
For example, the following code could be shortened significantly
// Old version $satisifes_predicate = false; foreach ($items as $item) { if (API::satisfiesCondition($item)) { $satisfies_predicate = true; break; } } if (!$satisfies_predicate) { throw new APIException("No matches found"); } // New version is much shorter and readable if (!any($items, fn($x) => API::satisfiesCondition($x))) { throw new APIException("No matches found"); }
That example doesn't have any suitable helpers already in the standard
library.
Using array_filter would unnecessarily call satisfiesCondition even after
the first item was found,
and array_search doesn't take a callback.A proposed implementation is https://github.com/php/php-src/pull/6053 -
it takes similar flags and param orders toarray_filter()
.Previous discussion was in https://externals.io/message/103357#103373
- New contributors to projects wouldn't know about any() and all() if it
was reimplemented with different semantics and only occasionally used
(e.g. MyArrayUtil::any()) in various projects)- If this was provided only in userland, there'd be low adoption and code
such as the first example would remain common.
If the standard library provided it, then polyfills would as well,
making cleaner code easier to write.Thanks,
- Tyson
Would it make sense, instead of having a third boolean parameter (causing
two parameters to be coupled together - already quite messy with existing
array functions) for any()
and all()
to just detect if the given
callback requires >1 parameter?
That would make this much simpler.
Marco Pivetta
Hi Marco Pivetta,
Would it make sense, instead of having a third boolean parameter (causing two parameters to be coupled together - already quite messy with existing array functions) for
any()
andall()
to just detect if the given callback requires >1 parameter?That would make this much simpler.
I find that to be inconsistent with how other internal functions accepting callbacks such as array_filter
behave.
It would also be unexpected if users or tooling wrap callbacks with other callbacks.
Developers would also expect func_get_args()
to work.
<?php
function my_none(iterable $values, callable $predicate) {
return all($values, fn(...$args) => $predicate(...$args));
}
// PHP can't distinguish between these when given `fn(...$args)`
// - would throw if passed 1 parameter
my_none($values, fn($value, $key) => $value == true || $key == 'x');
// - would throw if passed 2 parameters (too many)
my_none($values, 'is_string');
Thanks,
- Tyson
Hi internals,
The primitives any() and all() are a common part of many programming
languages and help in avoiding verbosity or unnecessary abstractions.
https://hackage.haskell.org/package/base-4.14.0.0/docs/Prelude.html#v:any
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
For example, the following code could be shortened significantly
// Old version $satisifes_predicate = false; foreach ($items as $item) { if (API::satisfiesCondition($item)) { $satisfies_predicate = true; break; } } if (!$satisfies_predicate) { throw new APIException("No matches found"); } // New version is much shorter and readable if (!any($items, fn($x) => API::satisfiesCondition($x))) { throw new APIException("No matches found"); }
That example doesn't have any suitable helpers already in the standard
library.
Using array_filter would unnecessarily call satisfiesCondition even
after the first item was found,
and array_search doesn't take a callback.A proposed implementation is https://github.com/php/php-src/pull/6053 -
it takes similar flags and param orders toarray_filter()
.Previous discussion was in https://externals.io/message/103357#103373
- New contributors to projects wouldn't know about any() and all() if
it was reimplemented with different semantics and only occasionally used
(e.g. MyArrayUtil::any()) in various projects)- If this was provided only in userland, there'd be low adoption and
code such as the first example would remain common.
If the standard library provided it, then polyfills would as well,
making cleaner code easier to write.Thanks,
- Tyson
I like this, but I do not like the flags. I don't think they're at all useful. A lot of the other discussion in the thread seems to be needlessly complicating it, too.
all() and any() only need return booleans. Their callbacks only need return booleans. That's the point. first() makes sense to add, and it would return the first value that matches.
For the callback itself, there is work to, hopefully, add partial function application to 8.1. (No idea if it will be successful, but the effort is in progress.) If so, the upshot is that turning an arbitrary function into a single-parameter function becomes silly easy, which means functions like this can just expect a single parameter callback and be done with it. No need for extra-args or flags or whatnot.
If you want to check the keys of an array, call array_keys()
first and use that.
if (any(array_keys($foo), fn($k) => $k %2)) { ... }
all(), any(), and first() all sound like good things to include, but let's not over-complicate them. We can do better today than we could in 1999...
--Larry Garfield
I like this, but I do not like the flags. I don't think they're at all useful. A lot of the other discussion in the thread seems to be needlessly complicating it, too.
all() and any() only need return booleans. Their callbacks only need return booleans. That's the point. first() makes sense to add, and it would return the first value that matches.
What would first() return on failure? Would it throw (inefficient)?
Would it set an optional output reference to distinguish between returning the value null from an iterable and the null from no matches?
An alternative use for a global function called first() would be to simply return the first element of an iterable, similar to the proposed array_value_first for arrays
For the callback itself, there is work to, hopefully, add partial function application to 8.1. (No idea if it will be successful, but the effort is in progress.) If so, the upshot is that turning an arbitrary function into a single-parameter function becomes silly easy, which means functions like this can just expect a single parameter callback and be done with it. No need for extra-args or flags or whatnot.
If you want to check the keys of an array, call
array_keys()
first and use that.
if (any(array_keys($foo), fn($k) => $k %2)) { ... }
That doesn't work for a predicate on the keys of a generator, e.g. if you want to stop calling the generator once the short-circuiting happens.
And there are occasionally situations where the predicate depends both on the key and the value
any($itemMap, fn($enabled, $itemId) => $enabled && meetsAdditionalCondition($itemId), ARRAY_FILTER_USE_BOTH)
Still, since there's debate over whether to even complicate this by filtering by key,
and what the best way is to implement filtering by key, I'll probably take out the int $flag = 0
part from the initial RFC.
Those can be added in subsequent RFCs.
Thanks,
- Tyson
On Sun, Aug 30, 2020 at 6:13 PM tyson andre tysonandre775@hotmail.com
wrote:
I like this, but I do not like the flags. I don't think they're at all
useful. A lot of the other discussion in the thread seems to be needlessly
complicating it, too.all() and any() only need return booleans. Their callbacks only need
return booleans. That's the point. first() makes sense to add, and it
would return the first value that matches.What would first() return on failure? Would it throw (inefficient)?
Would it set an optional output reference to distinguish between returning
the value null from an iterable and the null from no matches?
If it took the default value as well it could return that. While it's
useful in itself it also would enable you to pass a marker object and check
the identity of that to know if no matches have been found:
$none = new stdClass;
$element = first($collection, fn($elt) => ...);
if ($element === $none) {
// nothing found
}
--
Best regards,
Bruce Weirdan mailto:
weirdan@gmail.com
On Sun, Aug 30, 2020 at 6:13 PM tyson andre tysonandre775@hotmail.com
wrote:I like this, but I do not like the flags. I don't think they're at all
useful. A lot of the other discussion in the thread seems to be needlessly
complicating it, too.all() and any() only need return booleans. Their callbacks only need
return booleans. That's the point. first() makes sense to add, and it
would return the first value that matches.What would first() return on failure? Would it throw (inefficient)?
Would it set an optional output reference to distinguish between
returning the value null from an iterable and the null from no matches?If it took the default value as well it could return that. While it's
useful in itself it also would enable you to pass a marker object and check
the identity of that to know if no matches have been found:$none = new stdClass;
$element = first($collection, fn($elt) => ...);
if ($element === $none) {
// nothing found
}
Of course it should have been $element = first($collection, fn($elt) =>..., $none);
--
Best regards,
Bruce Weirdan mailto:
weirdan@gmail.com
Hi Bruce Weirdan,
If it took the default value as well it could return that. While it's useful in itself it also would enable you to pass > a marker object and check the identity of that to know if no matches have been found:
$none = new stdClass;
$element = first($collection, fn($elt) => ..., $none);
if ($element === $none) {
// nothing found
}
Oh, right, I'd forgotten about that option - that would work.
There's still the question of what it would do without a predicate.
Making the predicate mandatory in the initial proposal would help avoid the confusion of whether first() should behave like
array_filter()` and any() and filter for the first truthy value.
Calling it [iterable_]search_callback() or first_matching() or find() might help distinguish this from the reset()
/end()/next()/prev() family of functions - not happy with any of my naming ideas
Thanks,
- Tyson
Hi internals,
The primitives any() and all() are a common part of many programming languages and help in avoiding verbosity or unnecessary abstractions.
I would love to see this come to PHP. I also do a lot of Python
development and I really like its operators module, which provides
function equivalents to the intrinsic Python operators. Have a look:
https://docs.python.org/3/library/operator.html
Although the any() and all() functions are my favorites, having the full
range of operator functions would be great. That could ultimately yield
a proper counterpart to the range of array_* functions which support any
iterable in stead of just arrays. Think of a keys() function that is a
generalization of array_keys()
.
This reminds me of the iter library that Nikita created:
So yes, this can also be done in user space. Having it built into the
language has advantages though:
- High adoption rates, making lots of existing PHP code more concise
- Possibly better performance
Regarding performance: Since we already have opcodes for handling
operators, it may be possible to extend these to consume arbitrary
numbers of operands from iterables. Then, an operator function like
any() would compile into its equivalent operator opcode. Finally, that
opcode can probably be JIT compiled into a tight loop in machine code.
So a +1 from me for adding any() and all() but let us also consider the
general problem of lacking iterable support in PHP. Perhaps that could
be the subject of a followup RFC.
Regards,
Dik Takken
Hi Dik Takken,
I would love to see this come to PHP. I also do a lot of Python
development and I really like its operators module, which provides
function equivalents to the intrinsic Python operators. Have a look:https://docs.python.org/3/library/operator.html
Although the any() and all() functions are my favorites, having the full
range of operator functions would be great. That could ultimately yield
a proper counterpart to the range of array_* functions which support any
iterable in stead of just arrays. Think of a keys() function that is a
generalization ofarray_keys()
.
Operator overloading was the subject of https://wiki.php.net/rfc/userspace_operator_overloading#vote
which had a 38-28 vote, which didn't meet the 2/3 voting threshold.
This reminds me of the iter library that Nikita created:
So yes, this can also be done in user space. Having it built into the
language has advantages though:
- High adoption rates, making lots of existing PHP code more concise
- Possibly better performance
Strangely, if I remember correctly, I've had better performance looping inline and calling closures than calling array_map
when there were a large number of elements. I think opcache's inferences and the fact there's no switch
from internals back to the php vm was part of the reason for it.
(and the tracking of stack frames?)
The readability's my main reason for making it native. I'd wondered if there'd be a benefit to preloading where we replace C functions with native php functions (optionally JITted) if there's an expected benefit,
like HHVM does for parts of its standard library, but again out of scope.
Regarding performance: Since we already have opcodes for handling
operators, it may be possible to extend these to consume arbitrary
numbers of operands from iterables. Then, an operator function like
any() would compile into its equivalent operator opcode. Finally, that
opcode can probably be JIT compiled into a tight loop in machine code.
That's definitely something I'd want to see for array_map/array_filter.
There's the question of whether it'd be reasonable to omit stack frames or whether it's practical to create fake frames with parameter values
for debug_backtrace()
.
Larry Garfield also did some work on python-like list comprehensions,
though it's still in draft.
Automatically rewriting array_map()
, array_filter()
, all(), etc. to list comprehensions and then to JITted code
could see a nice performance benefit in benefit, but this is out of scope of the RFC I'm working on
See https://externals.io/message/104637
So a +1 from me for adding any() and all() but let us also consider the
general problem of lacking iterable support in PHP. Perhaps that could
be the subject of a followup RFC.
Part of the issue is what the return type would be.
For example, reduce(iterable, callable, $initial = null) could work well, but what should map() do
map(callable, iterable $values): iterable
- should it preserve keys, should it return an array|Generator, array|CustomTraversable, something depending on the object type, etc.
(Generator performance seems like it'd be worse than eager evaluation)
Regards,
Tyson
Hi Dik Takken,
I would love to see this come to PHP. I also do a lot of Python
development and I really like its operators module, which provides
function equivalents to the intrinsic Python operators. Have a look:https://docs.python.org/3/library/operator.html
Although the any() and all() functions are my favorites, having the full
range of operator functions would be great. That could ultimately yield
a proper counterpart to the range of array_* functions which support any
iterable in stead of just arrays. Think of a keys() function that is a
generalization ofarray_keys()
.Operator overloading was the subject of
https://wiki.php.net/rfc/userspace_operator_overloading#vote
which had a 38-28 vote, which didn't meet the 2/3 voting threshold.This reminds me of the iter library that Nikita created:
So yes, this can also be done in user space. Having it built into the
language has advantages though:
- High adoption rates, making lots of existing PHP code more concise
- Possibly better performance
Strangely, if I remember correctly, I've had better performance looping
inline and calling closures than calling array_map
when there were a large number of elements. I think opcache's
inferences and the fact there's no switch
from internals back to the php vm was part of the reason for it.
(and the tracking of stack frames?)The readability's my main reason for making it native. I'd wondered if
there'd be a benefit to preloading where we replace C functions with
native php functions (optionally JITted) if there's an expected benefit,
like HHVM does for parts of its standard library, but again out of
scope.Regarding performance: Since we already have opcodes for handling
operators, it may be possible to extend these to consume arbitrary
numbers of operands from iterables. Then, an operator function like
any() would compile into its equivalent operator opcode. Finally, that
opcode can probably be JIT compiled into a tight loop in machine code.That's definitely something I'd want to see for array_map/array_filter.
There's the question of whether it'd be reasonable to omit stack frames
or whether it's practical to create fake frames with parameter values
fordebug_backtrace()
.Larry Garfield also did some work on python-like list comprehensions,
though it's still in draft.
Automatically rewritingarray_map()
,array_filter()
, all(), etc. to
list comprehensions and then to JITted code
could see a nice performance benefit in benefit, but this is out of
scope of the RFC I'm working on
See https://externals.io/message/104637
I was just considering mentioning that. :-) I still would like to see comprehensions happen, but I'd need to partner with someone with more engine skill than I to actually implement it, plus the reception was somewhat lukewarm. I do think they're useful and would help with any/all/first type use cases, though.
If someone reading this is interested, you know where to find me...
--Larry Garfield
I was actually working on this sort of thing recently. Technically,
you can support all
, any
, and first
by using a single function:
function find_first(iterable $of, callable($value, $key): bool
$thatSatistifes): Iterator
It converts the $iterable into an Iterator, then calls the callback
for each key/value pair until one returns true, and then always
returns the iterator at the current position.
- This allows you to know both key and value when making a decision.
- By returning an iterator the caller can get both key and value.
- By returning an iterator it can handle both the empty case and not
found cases with $result->valid() === false. - By returning an iterator it might be useful for processing the
remainder of the list somehow.
I'm not sure that in practice it would be that friendly, but it's
worth pointing out for discussion at least.
Hi Levi Morrison,
I was actually working on this sort of thing recently. Technically,
you can supportall
,any
, andfirst
by using a single function:function find_first(iterable $of, callable($value, $key): bool
$thatSatistifes): IteratorIt converts the $iterable into an Iterator, then calls the callback
for each key/value pair until one returns true, and then always
returns the iterator at the current position.I'm not sure that in practice it would be that friendly, but it's
worth pointing out for discussion at least.
I'd find the find($iterable, $callable, $default = null): mixed
to be more user-friendly,
personally, and would worry about accidentally increasing memory usage if I left one of those iterables around as a saved value or a long-lived local variable.
There's plenty of time until the php 8.1 feature freeze, so I don't feel like increasing the scope of this RFC
(https://wiki.php.net/rfc/any_all_on_iterable)
even though I would find some version of find()
with a callback useful.
Thanks,
- Tyson