-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Proposal: Add iterable\any(iterable $input, ?callable $cb=null), all(...), none(...), find(...), reduce(...) #6053
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
52ed77c
to
f265f80
Compare
I suggest slightly different signatures, assuming we stay value-oriented: /**
* @template Element
* @psalm-param iterable<Element> $of
* @psalm-param callable(Element): bool $satisfy
*/
function all_values(iterable $of, callable $satisfy): bool
{
foreach ($of as $value) {
if (!$satisfy($value)) {
return false;
}
}
return true;
}
/**
* @template Element
* @psalm-param iterable<Element> $of
* @psalm-param callable(Element): bool $satisfies
*/
function any_value(iterable $of, callable $satisfies): bool
{
foreach ($of as $value) {
if ($satisfies($value)) {
return true;
}
}
return false;
}
// with named parameters
all_values(of: [1, 3, 5, 7], satisfy: 'is_odd');
any_value(of: [0, 2, 4, 6], satisfies: 'is_prime');
// without named parameters
all_values([1, 3, 5, 7], 'is_odd');
any_value([0, 2, 4, 6], 'is_prime'); The naming clarifies what |
ext/standard/array.c
Outdated
@@ -6389,3 +6389,155 @@ PHP_FUNCTION(array_combine) | |||
} ZEND_HASH_FOREACH_END(); | |||
} | |||
/* }}} */ | |||
|
|||
static inline void php_array_until(zval *return_value, HashTable *htbl, zend_fcall_info fci, zend_fcall_info_cache fci_cache, int stop_value) /* {{{ */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In .c
files the inline
should either be removed, or should be changed into zend_always_inline
depending on intent.
static inline void php_array_until(zval *return_value, HashTable *htbl, zend_fcall_info fci, zend_fcall_info_cache fci_cache, int stop_value) /* {{{ */ | |
static void php_array_until(zval *return_value, HashTable *htbl, zend_fcall_info fci, zend_fcall_info_cache fci_cache, int stop_value) /* {{{ */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was basing this on the surrounding code, such as php_search_array
used for in_array and array_search
I'd assume there would be a place where you'd want "inline depending on compiler's best guess at optimization level" but I'm not familiar with the why of zend_always_inline vs inline
/* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
* 0 = return boolean
* 1 = return key
*/
static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
{
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to specify inline
; a static
function in a .c
file is enough. If you want to be sure it's inlined, that's when you throw on zend_always_inline
.
ext/standard/array.c
Outdated
zend_fcall_info_cache fci_cache = empty_fcall_info_cache; | ||
|
||
ZEND_PARSE_PARAMETERS_START(1, 2) | ||
Z_PARAM_ZVAL(input) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we not have Z_PARAM_*
for iterable
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked and didn't find anything - https://php.net/iterator_map takes an iterator but not an iterable. I didn't see any .stub.php files using iterable (see build/gen_stubs.php change)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that I opened PR #6064: "Add Z_PARAM_ITERABLE and co" to address this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should now be able to use Z_PARAM_ITERABLE
as the PR merged.
Quite a few alternate suggestions have been made, including https://externals.io/message/111756#111760
https://github.com/nikic/iter was a userland library with similar iteration primitives which might be worth adopting analogous functions for - though I doubt future proposals for php-src would use generators implemented in C due to performance. It somewhat makes sense for iter_first() or iter_is_empty() for consistency if we expect a family of functions to be added, but iter_apply(), iter_all(), iter_any(), iter_keys(iterable): array seem unnecessarily verbose (that's not a strong preference for the short version, though) |
ext/standard/array.c
Outdated
} | ||
/* fallthrough */ | ||
default: | ||
/* Intentionally not converted to an exception */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment shouldn't be here :)
Hi, what's the reason for only passing values? I see the |
PHP 8.0+ throws an ArgumentCountError when an internal function is called with too many parameters. A future PHP version may also throw when a user-defined function is called with too many parameters, but a lot of code may rely on that. So passing extra parameters would make any() throw for type checks. Additionally, many programmers would expect php > echo PHP_VERSION;
8.0.1-dev
php > var_export(is_string('value', 'key'));
Warning: Uncaught ArgumentCountError: is_string() expects exactly 1 argument, 2 given in php shell code:1
Stack trace:
#0 php shell code(1): is_string('value', 'key')
#1 {main}
thrown in php shell code on line 1
php > var_export(array_filter(['key' => 'value'], 'is_string', ARRAY_FILTER_USE_BOTH));
Warning: Uncaught ArgumentCountError: is_string() expects exactly 1 argument, 2 given in php shell code:1
Stack trace:
#0 [internal function]: is_string('value', 'key')
#1 php shell code(1): array_filter(Array, 'is_string', 1)
#2 {main}
thrown in php shell code on line 1 |
1b4e5c0
to
e8a15fa
Compare
@TysonAndre Would you be willing to collaborate on another attempt, this time sticking it into |
I was rebasing this because I was planning on resuming the original attempt, which I never started the vote on. I'd postponed it because
|
You make a good point about moving it to Spl - all of the existing functions on Traversables I remember (iterator_to_array, iterator_apply, iterator_count) are in spl. So are classes (https://www.php.net/manual/en/spl.iterators.php). I wasn't thinking about that distinction since spl was always enabled and statically compiled since PHP 5.3, but it would make sense to clarify this would go in spl (for the manual) and update the implementation unless there's a reason not to put it in spl. Aside: https://wiki.php.net/rfc/any_all_on_iterable_straw_poll#vote was created to poll internals on the naming choice |
If we move it to the SPL there are other naming choices: I am sure the namespacing here is adequate and does not require a specific vote for it. I'm sort of a maintainer, and other sort-of maintainers are also in favor of it, so that part isn't an issue. (I think all the official maintainers have gone inactive). |
e8a15fa
to
d56916f
Compare
d56916f
to
1a677ec
Compare
Create iterator_every() and iterator_any() This has merge conflicts which were resolved in the subsequent commit(s). This commit was used as a basis for implementing an alternate approach to checking if all()/any() elements in an iterable satisfy a predicate. Cherry-picked from phpGH-3597 with modifications Combine helpers into any() and all(), modernize for php 8 Properly free the result of the callback if it was a refcounted type such as a string/array. Fix the default value of the callback. Co-Authored-By: bugreportuser Co-Authored-By: Tyson Andre <[email protected]>
Semantics are similar to array_reduce
And add checks for edge cases of Traversables
bdd1384
to
6a01897
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In your initial comment, the return types for find
and reduce
are showing bool
; can you fix that?
|
||
function none(iterable $iterable, ?callable $callback = null): bool {} | ||
|
||
function reduce(iterable $iterable, callable $callback, mixed $initial = null): mixed {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I strongly prefer removing $initial
and adding fold
which always requires it:
function fold(iterable $iterable, callable $callback, mixed $initial): mixed {}
function reduce(iterable $iterable, callable $callback): mixed {}
The fold
function doesn't throw on empty, and the reduce
will. This pattern exists in other languages, such as Kotlin.
I'm happy to do this work if you'll agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like the inconsistency with array_reduce (which has optional $initial=null) would have more objectors for making it harder to learn the language or switch code from array_reduce to *reduce
intuitively for beginners.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an error condition in reduce
where there is not an initial value and an empty iterable, and it should throw because there is no legal value we can return that isn't already possible in the reduction. We should not repeat the mistakes of the past. You argue in another comment that find
is useful in other languages, and yet you don't buy that same argument here? What gives?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this again, I'd agree my earlier proposal for reduce was a mistake and it's worth changing, adding fold and either removing reduce entirely or requiring a non-empty array.
The other argument was about including a function, not a change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can work on fold
tonight.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you okay with these signatures and semantics for fold
and reduce
?
I imagine there is some discussion to be had for naming the parameters to make sure named parameters is a good experience, so let me know what you think. The name $into
I picked from Swift. I used $by
because it's short but not an abbreviation like acc
; my quick glance in other languages' docs didn't turn up anything better.
namespace Iterable;
/**
* @template Element
* @template Result
* @param iterable<Element> $seq
* @param Result $into
* @param callable(Result, Element): Result $by
* @return Result
*/
function fold(iterable $seq, mixed $into, callable $by): mixed {
foreach ($seq as $value) {
$into = $by($into, $value);
}
return $into;
}
/** Private helper, wouldn't actually be exposed.
* @template Key
* @template Value
* @param iterable<Key, Value> $seq
* @return \Iterator<Key, Value>
*/
function to_iterator(iterable $seq): \Iterator {
if (\is_array($seq)) {
return new \ArrayIterator($seq);
} elseif ($seq instanceof \Iterator) {
return $seq;
} else {
assert($seq instanceof \IteratorAggregate);
return namespace\to_iterator($seq->getIterator());
}
}
/**
* @template Element
* @param iterable<Element> $seq
* @param callable(Element, Element): Element $by
* @return Element
* @throws \ValueError if $seq does not have at least 1 element
*/
function reduce(iterable $seq, callable $by): mixed {
$iterator = namespace\to_iterator($seq);
$iterator->rewind();
if (!$iterator->valid()) {
throw new \ValueError("parameter \$seq to reduce... was empty");
}
$into = $iterator->current();
for ($iterator->next(); $iterator->valid(); $iterator->next()) {
$into = $by($into, $iterator->current());
}
return $into;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For fold, I think having the callback third would be hard to remember when it's second in other reduce() function
The inner implementation seem reasonable enough. I assume the ArrayObject is just for illustrating the behavior and not the internal implementation
$seq seems like an harder to remember naming choice compared to $array/$iterable used elsewhere - https://www.php.net/manual/en/function.iterator-apply.php and https://www.php.net/manual/en/function.array-walk.php - especially for non-english speakers
PHP's already using $initial for https://www.php.net/manual/en/function.array-reduce.php and I don't see a strong reason to switch to a different name - initial's been used elsewhere (e.g. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inner implementation seem reasonable enough. I assume the ArrayObject is just for illustrating the behavior and not the internal implementation
The ArrayIterator
is just for showing behavior, yes. Notably, this will not pass NULL
as the first parameter to the callback on the very first time it is called, unlike array_reduce
and what this PR currently does.
As an example with the data set [1, 3, 5, 7]
:
$result = reduce([1, 3, 5, 7], function ($into, $value) {
$retval = $into + $value;
echo "fn ({$into}, {$value}) => {$retval}\n";
return $retval;
});
This will print:
fn (1, 3) => 4
fn (4, 5) => 9
fn (9, 7) => 16
And not:
fn(, 1) => 1
fn (1, 3) => 4
fn (4, 5) => 9
fn (9, 7) => 16
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented the changes to reduce
on this branch: https://github.com/morrisonlevi/php-src/tree/levi/any-all-iterable-checks. For some reason it wouldn't let me select your fork as the merge-base, so I didn't open a PR, but you can look at the last two commits. I did not yet add fold
.
|
||
function reduce(iterable $iterable, callable $callback, mixed $initial = null): mixed {} | ||
|
||
function find(iterable $iterable, callable $callback, mixed $default = null): mixed {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove this one? I'd rather add filter, map, and flatmap if we need more functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
find() is also useful and many other programming languages include both (js, haskell, etc.)
For example, if you have an array of a million elements and only want the first match, it is much more efficient to call find() if the iterable contains a matching value (and there would be less service calls and db calls) compared to reset(array_filter(...))
Additionally, filter() and map() would be waiting on the existence of CachedIterable when it starts, because Traversables can have repeated keys yield 'key' => 1; yield 'key' => 2;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally, filter() and map() would be waiting on the existence of CachedIterable when it starts, because Traversables can have repeated keys
yield 'key' => 1; yield 'key' => 2;
This does not require a CachedIterable; they can return any iterator.
Edit: on second thought, they should not return a cached iterable. These routines are often chained together; if every piece of the chain caches their results, it will balloon memory usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean that there's nothing in the standard library yet that supports rewinding, counting, and especially arbitrary offset access
It seems much more difficult to use without the support for rewindability, random/repeated offset access, countability, etc.
But yes, I suppose you could hide the implementation entirely with InternalIterator and only support a single iteration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit: on second thought, they should not return a cached iterable. These routines are often chained together; if every piece of the chain caches their results, it will balloon memory usage.
CachedIterable is basically the name chosen for a rewindable immutable key-value sequence. It isn't cached permanently, it has a regular object lifetime. I'm referring to https://wiki.php.net/rfc/cachediterable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CachedIterable is basically the name chosen for a rewindable immutable key-value sequence. It isn't cached permanently, it has a regular object lifetime. I'm referring to https://wiki.php.net/rfc/cachediterable
Yes, I am specifically saying they should not return this object. It will ballon memory usage to do it that way. Think about it; you have a map + filter plus some terminator like first_n
with n=100. Filter and map will each hold at least 100 values in memory that shouldn't be there while calculating the first_n
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I am specifically saying they should not return this object. It will ballon memory usage to do it that way. Think about it; you have a map + filter plus some terminator like
first_n
with n=100. Filter and map will each hold at least 100 values in memory that shouldn't be there while calculating thefirst_n
.
- Many end users would want the standard library to return something that could be iterated over multiple times - this was a source of bugs in spl classes such as https://www.php.net/arrayobject prior to them being switched to iterables
$foo = map(...);
foreach ($foo as $i => $v1) {
foreach ($foo as $i => $v2) {
if (some_pair_predicate($v1, $v2)) {
// do something
}
}
}
- Library/application authors that are interested in lazy generators could use or implement something such as https://github.com/nikic/iter instead - my opinion is that the standard library should provide something that is easy to understand, debug, serialize or represent, etc.
- It would be harder to understand why SomeFrameworkException is thrown in code unrelated to that framework when it is passed to some function that accepts a generic iterable, and harder to write correct exception handling for it if done in a lazy generation style
- It is possible to implement a lazy version of CachedIterable that only loads values as needed - but I hadn't proposed it due to doubts that 2/3 of voters would consider it widely useful enough to be included in php rather than as a userland or PECL library
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iterables are made from arrays and generators. Since there is already a large body of functions which work with arrays, it seems reasonable to assume they would use an Iterable
part of the standard library because they also use iterators/generators. One of the main points of generators is to reduce memory. We shouldn't add a corpus of iterable functions that will increase memory; that works directly against the goals of the iterable feature!
No, it's much better to compose something if you want it to be eager, e.g. $foo = CachedIterable::new(map(...$args));
We can include this in the examples in the docs for filter, map, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iterables are made from arrays and generators
Iterables are made from arrays and Traversable objects such as SplObjectStorage, ArrayObject, user-defined data structures, and Generators
We shouldn't add a corpus of iterable functions that will increase memory; that works directly against the goals of the iterable feature!
In common use cases, the memory increase may be small, especially if the lifetime of the variable or temporary expression holding the result is short.
Additionally, a lazy iterable would require keeping a reference to the previous (possibly lazy) iterable, and the iterables/arrays those reference - if the initial iterable is larger than the result then eagerly evaluating $var = map($cb, temporary())
would end up saving memory
No, it's much better to compose something if you want it to be eager, e.g.
$foo = CachedIterable::new(map(...$args));
We can include this in the examples in the docs for filter, map, etc.
That seems error prone and I'm personally opposed to that.
PHP has typically been imperitive rather than functional, and focused on "cater[ing] to the skill-levels and platforms of a wide range of users" as RFC authors are repeatedly reminded in https://wiki.php.net/rfc/template and my interpretation of that is that imperitive would be much more acceptable (aside: The loosely typed language part seems less applicable nowadays)
PHP is and should remain:
- a pragmatic web-focused language
- a loosely typed language
- a language which caters to the skill-levels and platforms of a wide range of users
Lazy data structures would be easy to misuse (consume twice, attempt to serialize or encode, (or var_dump or inspect with Xdebug), easier to attempt to log the full iterable(consume twice) etc) without (or even with) linters and static analyzers, so this really doesn't seem like catering to a wide range of users.
Explicitly using a different family of functions to act on generators internally would probably make more sense than being the default, e.g. https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#findFirst-- and https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html (Streams are separate from java.util.Collection in java, javascript eagerly evaluates https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Map, etc)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's much better to compose something if you want it to be eager, e.g.
$foo = CachedIterable::new(map(...$args));
We can include this in the examples in the docs for filter, map, etc.
Having the eager version be longer than the lazy version (instead of shorter or the same length) would also encourage the use of the lazy version, which I'd objected to for being error prone and easy to misuse.
zend_always_inline should only be done if: - It's been proven to be a hotspot - It's in a header file
Iterable\reduce() no longer takes an initial value; it pulls it from the iterable. If there isn't at least one value, it throws. Expect another commit with Iterable\fold which has a required $initial parameter, which does not throw if the iterable is empty.
Looks like the |
I fixed one trivial issue with the wrong enum type; again pushed to my branch. |
Fixed, also changed back from ZVAL_COPY to ZVAL_COPY_VALUE to avoid a leak reported in |
The scope has increased significantly from 2 functions https://wiki.php.net/rfc/any_all_on_iterable#changelog - and with the namespacing discussion it seems to me like the functionality would need closer to a 5/6 majority (or higher) to pass after accounting for voters strongly opposed to choosing a given namespacing pattern for future additions to core Based on early results of https://wiki.php.net/rfc/cachediterable_straw_poll#namespace_choices , and https://wiki.php.net/rfc/any_all_on_iterable#straw_poll and https://wiki.php.net/rfc/any_all_on_iterable_straw_poll_namespace#vote I'm seriously doubting this would meet the 2/3rd majority required for an RFC to pass.
|
https://wiki.php.net/todo/php81#timetable indicates the feature freeze is in July 20th, in 5 weeks in 8.1.0beta1 - I'd forgotten it was this soon. https://wiki.php.net/rfc/cachediterable_straw_poll#namespace_choices looks as if an RFC may pass, but may fail again without a much larger group of standard library iterable functions to pass (given other objections to the functionality or choice of namespace) With rfcs requiring 2 weeks of discussion and 2 weeks of voting it make more sense to target 8.2 - if any amendments (new arguments, changes to default behaviors, renaming, etc) are proposed, then there would be much more time to discuss them. |
Hi @TysonAndre, I see also that this function could save some memory and time when users incorrectly search through arrays with loops without early return. Do you have any plans about implementing/coming back to this RFC? |
No - https://wiki.php.net/rfc/any_all_on_iterable had just under 50% of a vote, and RFCs require a 2/3 majority to pass. The no voters objected to it both not having enough functionality and the choice of namespace (preferring global or other) - so even if it had enough functionality added, I'm still concerned it would fail from nothing really being different about namespaces (no new groups of global function functionality were added to php-src). The iterable functionality can be found at https://github.com/TysonAndre/pecl-teds/#iterable-functions under a different namespace.
This RFC was more about saving developer time, and having a common abstraction that can be used across all projects, rather than various choices of composer/PECL options that differ across projects. For anything that calls callbacks on each element of an array, a userland solution is probably faster, because it avoids the need to switch from C back to the php vm repeatedly. (If there's no callback, then the native implementation would be faster) find_first() is find()? |
https://wiki.php.net/rfc/iterator_xyz_accept_array passed for php 8.2, and iterator_to_array and iterator_count now accept iterables in general, so there is now an existing group of 2 functions with the same naming pattern for functionality acting on iterables Though that would get confusing and the names are misleading - it might be nice to have some form of |
EDIT (2021-06-01): The namespace changed from PHP\iterable\ to iterable\ and several functions were added
TODO: Change namespace to
Iterable\
This is an alternative approach to https://externals.io/message/103357 and https://github.com/php/php-src/pull/3597/files by
bugreportuser
with the following changes:
bool
, unlike array_filter and array_map.Unify to any() and all() for arrays and Traversables
iterable\
after discussion of whether PHP should start using namespaces for new collections of internal functionality and collecting feedback on namespace choices - https://wiki.php.net/rfc/any_all_on_iterable_straw_poll_namespace#vote - After that vote was held, a different RFC https://wiki.php.net/rfc/namespaces_in_bundled_extensions passed forbidding vendored namespaces andSpl\
The following can be used as a polyfill (and also documents how this is meant to work)
For example, the following code could be shortened significantly:
Potential future scope:
Iterable\apply(iterable $input, callable(mixed):unused $callback)
(alternate names: walk/foreach_value) - Unlikearray_walk
, this does not convert array values to references and should avoid the resulting memory usage increase. Unlike https://www.php.net/iterator_apply - this passes the value - closure use variables are newer than iterator_apply.Iterable\map(iterable $input, callable(mixed):mixed $callback): ImmutableKeyValueSequence
(same arg order as ARRAY_FILTER_USE_BOTH)Iterable\filter_entries(iterable $input, callable(mixed $value, mixed $key):bool $callback): ImmutableKeyValueSequence
Iterable\filter_values(iterable $input, callable(mixed $value):unused $callback = null): array
(discards keys and returns a list of values that satisfy the predicate (or are truthy) (with keys 0,1,2,3...))Iterable\filter_keys(iterable $input, callable(mixed $key):bool $callback = null): array
Iterable\is_empty(iterable $input): bool
Iterable\keys(iterable $input): array
(like array_keys)Iterable\values(iterable $input): array
(like array_values/iterator_to_array but working on both. returns a list of values)Iterable\sorted_values(iterable $input): array
(like sort but (1) returns a sorted array instead of taking a reference, (2) also acts on Traversables)Iterable\contains_value(iterable $input, mixed $value): bool
(contains_key seems redundant with ArrayAccess/array_key_exist)slice/chunk/flip