rfc:any_all_on_iterable

This is an old revision of the document!


PHP RFC: any() and all() on iterables

Introduction

The primitives any() and all() are a common part of many programming languages and help in avoiding verbosity or unnecessary abstractions.

For example, the following code could be shortened significantly.

// The old version
$satisifes_predicate = false;
foreach ($items as $item) {
    // Performs DB operations or external service requests, stops on first match by design.
    if (API::satisfiesCondition($item)) {
        $satisfies_predicate = true;
        break;
    }
}
if (!$satisfies_predicate) {
    throw new APIException("No matches found");
}
// more code....
// The new version is much shorter, readable, and easier to review,
// without creating temporary variables or helper functions that are used in only one place.
 
// Performs DB operations or external service requests, stops on first match by design.
if (!any($items, fn($x) => API::satisfiesCondition($x))) {
    throw new APIException("No matches found");
}

Proposal

Add the following functions to php's standard function set.

/** Determines whether any element of the iterable satisfies the predicate. */
function any(iterable $input, ?callable $callback = null) {
    foreach ($input as $v) {
        if ($callback !== null ? $callback($v) : $v) {
            return true;
        }
    }
    return false;
}
/** Determines whether all elements of the iterable satisfy the predicate */
function all(iterable $input, ?callable $callback = null) {
    foreach ($input as $v) {
        if (!($callback !== null ? $callback($v) : $v)) {
            return false;
        }
    }
    return true;
}

This proposal recommends adding any() and all() to the standard library instead of a PECL or composer library for the following reasons

  1. New contributors to projects wouldn't know about any() and all() if it was reimplemented in various composer libraries or util.php files with different semantics/names and only occasionally used.
  2. If this was provided only in userland, there'd be low adoption and code such as the above example (API::somePredicate()) would remain common.
  3. If the standard library provided it, then polyfills for newer php functionality could adopt this as well, making cleaner code easier to write.

Backward Incompatible Changes

Any userland functions called any() and all() in the global namespace would cause duplicate function errors

Proposed PHP Version(s)

8.1

Future Scope

Add int $flag = 0?

Similar to array_filter, int $flag = 0 could be used to control which parameters get passed to the predicate such as ARRAY_FILTER_USE_BOTH and ARRAY_FILTER_USE_KEY.

Because there was discussion of whether the ability to pass keys was widely useful and multiple approaches that could be used to pass the iterable key, this functionality was left out of this RFC. See https://externals.io/message/111711#111721

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

Proposed Voting Choices

Add any(iterable $input, ?callable $callback = null) and all(...) (yes/no, requiring 2/3 majority)

References

- https://externals.io/message/111711 “Proposal: Adding functions any(iterable $input, ?callable $cb = null, int $use_flags=0) and all(...)” - https://externals.io/message/103357

Rejected Features

Keep this updated with features that were discussed on the mail lists.

rfc/any_all_on_iterable.1598804186.txt.gz · Last modified: 2020/08/30 16:16 by tandre