rfc:any_all_on_iterable
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionLast revisionBoth sides next revision | ||
rfc:any_all_on_iterable [2020/08/31 23:51] – tandre | rfc:any_all_on_iterable [2021/02/22 15:28] – tandre | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== PHP RFC: any() and all() on iterables ====== | + | ====== PHP RFC: PHP\iterable\any() and all() on iterables ====== |
- | * Version: 0.2 | + | * Version: 0.6 |
* Date: 2020-08-30 | * Date: 2020-08-30 | ||
* Author: Tyson Andre, tandre@php.net | * Author: Tyson Andre, tandre@php.net | ||
- | * Status: | + | * Status: |
* First Published at: https:// | * First Published at: https:// | ||
* Implementation: | * Implementation: | ||
Line 10: | Line 10: | ||
The primitives '' | The primitives '' | ||
+ | |||
- Haskell: https:// | - Haskell: https:// | ||
- JavaScript: https:// | - JavaScript: https:// | ||
- Python: https:// | - Python: https:// | ||
+ | - Ruby: https:// | ||
- Java 8(Stream): https:// | - Java 8(Stream): https:// | ||
+ | - C++: https:// | ||
For example, the following code could be shortened significantly: | For example, the following code could be shortened significantly: | ||
Line 20: | Line 23: | ||
<code php> | <code php> | ||
// The old version | // The old version | ||
- | $satisifes_predicate | + | $satisifies_predicate |
foreach ($item_list as $item) { | foreach ($item_list as $item) { | ||
// Performs DB operations or external service requests, stops on first match by design. | // Performs DB operations or external service requests, stops on first match by design. | ||
Line 39: | Line 42: | ||
// Performs DB operations or external service requests, stops on first match by design. | // Performs DB operations or external service requests, stops on first match by design. | ||
- | if (!any($item_list, | + | if (!\PHP\iterable\any($item_list, |
throw new APIException(" | throw new APIException(" | ||
} | } | ||
Line 46: | Line 49: | ||
===== Proposal ===== | ===== Proposal ===== | ||
- | Add the functions '' | + | Add the functions '' |
+ | (The namespace '' | ||
+ | |||
+ | **The implementation is equivalent to the following polyfill:** | ||
<code php> | <code php> | ||
- | /** Determines whether any element of the iterable satisfies the predicate. */ | + | namespace PHP\iterable; |
- | function any(iterable $input, ?callable $callback = null) { | + | |
+ | /** | ||
+ | * Determines whether any element of the iterable satisfies the predicate. | ||
+ | * | ||
+ | * | ||
+ | * If the value returned by the callback is truthy | ||
+ | * (e.g. true, non-zero number, non-empty array, truthy object, etc.), | ||
+ | * this is treated as satisfying the predicate. | ||
+ | * | ||
+ | * @param iterable $input | ||
+ | * @param null|callable(mixed): | ||
+ | */ | ||
+ | function any(iterable $input, ?callable $callback = null): bool { | ||
foreach ($input as $v) { | foreach ($input as $v) { | ||
if ($callback !== null ? $callback($v) : $v) { | if ($callback !== null ? $callback($v) : $v) { | ||
Line 58: | Line 76: | ||
return false; | return false; | ||
} | } | ||
- | /** Determines whether all elements of the iterable satisfy the predicate */ | + | </ |
- | function all(iterable $input, ?callable $callback = null) { | + | <code php> |
+ | /** | ||
+ | * Determines whether all elements of the iterable satisfy the predicate. | ||
+ | * | ||
+ | * If the value returned by the callback is truthy | ||
+ | * (e.g. true, non-zero number, non-empty array, truthy object, etc.), | ||
+ | * this is treated as satisfying the predicate. | ||
+ | * | ||
+ | * @param iterable $input | ||
+ | * @param null|callable(mixed): | ||
+ | */ | ||
+ | function all(iterable $input, ?callable $callback = null): bool { | ||
foreach ($input as $v) { | foreach ($input as $v) { | ||
if (!($callback !== null ? $callback($v) : $v)) { | if (!($callback !== null ? $callback($v) : $v)) { | ||
Line 69: | Line 98: | ||
</ | </ | ||
- | This proposal recommends adding '' | + | This proposal recommends adding '' |
- | - New contributors to projects wouldn' | + | - New contributors to projects wouldn' |
- If this was provided only in userland, there' | - If this was provided only in userland, there' | ||
- If the standard library provided it, then polyfills for newer php functionality could adopt this as well, making cleaner code easier to write. | - If the standard library provided it, then polyfills for newer php functionality could adopt this as well, making cleaner code easier to write. | ||
+ | |||
+ | ==== Implementation Details ==== | ||
+ | |||
+ | When '' | ||
+ | |||
+ | When '' | ||
+ | |||
+ | |||
+ | <code php> | ||
+ | php > var_export(PHP\iterable\any([false])); | ||
+ | false | ||
+ | php > var_export(PHP\iterable\any([true])); | ||
+ | true | ||
+ | php > var_export(PHP\iterable\any([0])); | ||
+ | false | ||
+ | php > var_export(PHP\iterable\any([1])); | ||
+ | true | ||
+ | php > var_export(PHP\iterable\any([0], | ||
+ | false | ||
+ | php > var_export(PHP\iterable\any([1], | ||
+ | true | ||
+ | |||
+ | php > var_export(PHP\iterable\all([true, | ||
+ | true | ||
+ | php > var_export(PHP\iterable\all([1, | ||
+ | true | ||
+ | php > var_export(PHP\iterable\all([true, | ||
+ | false | ||
+ | php > var_export(PHP\iterable\all([1, | ||
+ | false | ||
+ | php > var_export(PHP\iterable\all([1, | ||
+ | false | ||
+ | </ | ||
+ | |||
+ | ==== Secondary Vote: any()/all() or any_value()/ | ||
+ | |||
+ | A secondary vote will be held on whether to name this '' | ||
+ | |||
+ | PHP is unique in that the primitive array-like type '' | ||
+ | Existing function names vary in whether the fact that they only act on values is explicitly included in the name. | ||
+ | |||
+ | Many other programming languages have gone with a short name for the default of checking if a value is in a collection. | ||
+ | |||
+ | < | ||
+ | The primitives '' | ||
+ | |||
+ | - Haskell: https:// | ||
+ | - JavaScript: https:// | ||
+ | - Python: https:// | ||
+ | - Ruby: https:// | ||
+ | - Java 8(Stream): https:// | ||
+ | - C++: https:// | ||
+ | </ | ||
+ | |||
+ | Benefits of a shorter name: | ||
+ | |||
+ | - Conciseness for the most common use case of checking whether a predicate is true for any/all values of an iterable array/ | ||
+ | - Consistency with some other functions such as '' | ||
+ | - Potential to use $flags to extend this to support less common use cases like '' | ||
+ | |||
+ | Benefits of a longer name: | ||
+ | |||
+ | - A longer name would be more descriptive and make it easier to understand what code is doing. | ||
+ | - This makes it likely that in the future for iterable functionality, | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
- | Any userland functions called '' | + | Any userland functions called '' |
+ | Because the [[https:// | ||
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
Line 125: | Line 219: | ||
Calling it '' | Calling it '' | ||
- | ===== Proposed Voting Choices | + | ===== Discussion |
- | Add '' | + | ==== Alternative names ==== |
+ | |||
+ | '' | ||
+ | |||
+ | < | ||
+ | I suggest slightly different signatures, assuming we stay value-oriented: | ||
+ | |||
+ | <code php> | ||
+ | // ...omitted | ||
+ | |||
+ | // with named parameters | ||
+ | all_values(of: | ||
+ | any_value(of: | ||
+ | |||
+ | // without named parameters | ||
+ | all_values([1, | ||
+ | any_value([0, | ||
+ | </ | ||
+ | The naming clarifies what any and all are about--the values--and leaves room for naming functions that are key or key/value oriented. | ||
+ | </ | ||
+ | |||
+ | '' | ||
+ | |||
+ | < | ||
+ | The main thing I'm concerned about is that once we start extending this | ||
+ | area (I assume that any & all are not going to be the last additions in | ||
+ | this space) we will quickly run into function names that are either too | ||
+ | generic or outright collide. For example, what if we want to add an | ||
+ | iterator-based version of range()? Do we really want to be forced to pull | ||
+ | a Python and call it xrange()? That's about as good as real_range()... | ||
+ | |||
+ | As such, I think it's important to prefix these somehow, though I don' | ||
+ | care strongly how. Could be iter_all() or iterable_all(). We might even | ||
+ | make it iterator_all() if we also adjust other existing iterator_* | ||
+ | functions to accept iterables. I'd also be happy with iter\all() or | ||
+ | iterable\all(), | ||
+ | </ | ||
+ | |||
+ | Because '' | ||
+ | This also allows potentially supporting '' | ||
+ | |||
+ | Initially, the proposal was to add this in the global scope as '' | ||
+ | |||
+ | ==== Add find_first() instead? ==== | ||
+ | |||
+ | < | ||
+ | I was actually working on this sort of thing recently. Technically, | ||
+ | you can support all, any, and first by using a single function: | ||
+ | |||
+ | <code php> | ||
+ | function find_first(iterable $of, callable($value, | ||
+ | </ | ||
+ | |||
+ | It converts the '' | ||
+ | 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 '' | ||
+ | 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. | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Vote ===== | ||
+ | |||
+ | Add '' | ||
+ | |||
+ | Voting started on 2021-02-08 and ended on 2021-02-22. | ||
+ | |||
+ | <doodle title=" | ||
+ | * Yes | ||
+ | * No | ||
+ | </ | ||
+ | |||
+ | \\ The following secondary vote will be used to decide between '' | ||
+ | |||
+ | <doodle title=" | ||
+ | * any()/ | ||
+ | * any_value()/ | ||
+ | </ | ||
+ | |||
+ | ==== Straw Poll ==== | ||
+ | |||
+ | <doodle title=" | ||
+ | * Too small in scope | ||
+ | * Object to the choice of namespace | ||
+ | * Prefer the global namespace | ||
+ | * Confused about the implementation | ||
+ | * Prefer userland solutions | ||
+ | * Other | ||
+ | * Voted for this RFC | ||
+ | </ | ||
===== References ===== | ===== References ===== | ||
- https:// | - https:// | ||
- | - https:// | + | - https:// |
+ | - https:// | ||
+ | - [[rfc: | ||
+ | - [[rfc: | ||
===== Rejected Features ===== | ===== Rejected Features ===== | ||
Adding flags like [[https:// | Adding flags like [[https:// | ||
+ | |||
+ | ===== Changelog ====== | ||
+ | |||
+ | * 0.3: Add more quotes | ||
+ | * 0.4: Change name to '' | ||
+ | * 0.5: Add straw poll | ||
+ | * 0.6: Add examples of how this works, add in missing return type, clarify treatment of predicate $callback return type |
rfc/any_all_on_iterable.txt · Last modified: 2021/06/13 14:36 by tandre