====== Straw poll: Using namespaces for *any() and *all() on iterables ====== * Version: 0.2 * Date: 2021-01-05 * Author: Tyson Andre, tandre@php.net * Status: Closed * First Published at: http://wiki.php.net/rfc/any_all_on_iterable_straw_poll_namespace ===== Introduction ===== The primitives ''*any*()'' and ''*all*()'' are a common part of many programming languages and help in avoiding verbosity or unnecessary abstractions. - Haskell: https://hackage.haskell.org/package/base-4.14.0.0/docs/Prelude.html#v:any - JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some - Python: https://docs.python.org/3/library/functions.html#all - Ruby: https://apidock.com/ruby/Enumerable/any%3F - Java 8(Stream): https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#allMatch-java.util.function.Predicate- - C++: https://www.cplusplus.com/reference/algorithm/any_of/ Before adding this, I'd like to see what others think about the choice of naming pattern. In a previous poll, the option ''iterable_any()'' was preferred over ''iter_any()''/''any()'', but several responses were moderately or strongly in favor of namespaces. - I plan to propose additional internal functions for working with iterables if this succeeds, and would want to be sure this is the best name choice going forwards. - Additionally, this has been an opportunity for measuring overall interest in adopting namespaces for brand new categories of functionality - existing methods work on Traversables (''iterator_*'') or arrays (''array_*''), but generally not both. ===== Discussion ===== ==== Arguments for voting again on namespaces ==== https://externals.io/message/112558
From Levi Morrison: I want to re-iterate my opinion on this discussion thread: anything with a prefix is a hard-no from me. Namespaces are literally designed for this, and I will not vote "yes" to ''iter_all'', ''iterable_all'', etc, no matter what the prefix is. Anything without a namespace is a no from me. I'm flexible on many other points, but not this one. It's 2020 (almost 2021); let's use namespaces for what they were designed for. This is a perfect opportunity; they work on more than just arrays so using the ''array_'' prefix for consistency doesn't apply.
https://externals.io/message/112558#112588
Hey Tyson, I know I'm being stubborn, but we have namespaces, but also an "old guard" that doesn't understand its own programming language 🤷‍♀️
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't 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(), but that gets us back into namespacing discussions :) Regards, Nikita
==== Arguments for Spl\ ==== This poll includes one of the naming options suggested by Levi Morrison and uses the suggested namespace for alternative options.
> I could start a second straw poll and wait for those results if you had a proposal for (a namespace) in mind. Simply `Spl\all_values`. I don't think there's a need for another namespace component: 1) The SPL can disambiguate within itself. 2) It _shouldn't_ become the dumping ground of PHP.
''Spl'' was chosen as a namespace because: - Existing iterator/iterable functionality such as ''iterator_*'' and data structures were placed in the ''spl'' extension, and any/all would be as well. - Existing classes are named SplObjectStorage, SplFixedArray, etc. (e.g. for ''ReflectionClass->getName()'') - using the ''Spl'' casing would be consistent within the [[https://www.php.net/spl||spl module]]. - It is less prone to conflicts than ''iter'' - e.g. https://github.com/nikic/iter. - The ''Spl\'' namespace can be reused for new classes/functions if they belong in the ''spl'' module. (I have no plans to move existing functions/classes to that namespace) https://externals.io/message/112558#112834
From Levi Morrison: I want to make a case for `Spl`. Aside from autoloading (which really ought to be in core but since "spl" is literally in the name of those functions it's kind of stuck), the SPL is mostly data structures and iterator related functionality. It makes perfect sense to me that iterator related behavior like these iterable functions being proposed should be included in the SPL. The `Spl` namespace is short, and its brevity doesn't lose any meaning because the SPL has been around in core since PHP 5.3 (I think?). ... One of the arguments against using the SPL is that much of its existing design is... well, weird and sub-optimal in many cases. At least a few people recommend against using SPL data structures and iterators because of this. However, I think the `Spl` namespace can separate these well enough.
==== Arguments for PHP\ ==== https://www.php.net/manual/en/language.namespaces.rationale.php reserved the uppercase namespace PHP\ for internal use. https://wiki.php.net/rfc/php_namespace_policy was an earlier (declined) proposal that had suggested the use of PHP\SubNS for internal features. https://externals.io/message/112558#112841
FWIW as iterables and the likes relate to engine features, the PHP namespace policy RFC would have considered them ideal candidates for \PHP\SubNS rather than \Ext\SubNS Mark Randall
From Nikita Popov: https://externals.io/message/112558#112780
Using just the SPL namespace (that is, SPL\any) makes the SPL namespace a dumping ground for everything, as you said. Once you introduce an additional meaningful namespace in the form of SPL\iterable\any, you are better off either dropping the SPL part and arriving at iterable\any, or replacing SPL with something more sensible and arriving at PHP\iterable\any.
==== Arguments for/against iterable\ in namespace ==== https://externals.io/message/112558#112773
I'm happy to have these functions namespaced, but I'm not sure the suggestion to namespace them under Spl makes sense. This functionality has fairly little to do with the SPL as it is now and to be honest, by now there is quite a bit of ... stigma associated with functionality that resides in SPL. I would suggest using iterable\any and iterable\all as the names if we want to go down this route. iterable_any and iterable_all were the by far most popular choices on the previous poll, and these are just the namespaced variants thereof. Regards, Nikita
From https://externals.io/message/112558#112832 I've also realized that ''use prefix\iterable'' would be mildly inconvenient for users of PHP and for tooling (refactoring tools, IDEs, etc) for PHP, but still include it as an option because it's **unlikely to be a problem in practice and the non-namespaced prefix iterable_ was preferred in the previous poll.** php > namespace iterable { function my_any(...$args) {echo "in my_any() polyfill\n";}} php > \iterable\my_any(); // can be called with the fully qualified name in my_any() polyfill php > namespace X { use iterable\my_any; my_any(); /* using individual functions works and is how namespaced functions are often used */ } in my_any() polyfill php > namespace { iterable\my_any(); } in my_any() polyfill php > namespace W { echo iterable::class; /* Edge cases that should not be a problem in practice, but mentioned for completeness */ } W\iterable php > namespace X { use iterable; } Fatal error: Cannot use iterable as iterable because 'iterable' is a special class name in php shell code on line 1 php > namespace X { use PHP\iterable; } Fatal error: Cannot use PHP\iterable as iterable because 'iterable' is a special class name in php shell code on line 1 php > namespace X { use iterable as iter; iter\my_any(); } in my_any() polyfill ===== Vote ===== Voting started on 2021-01-19 and ended on 2021-02-02. Of the top two options, 12 voters had ranked "PHP\iterable" before the global namespace (including not listing the latter in their vote). 11 voters had ranked the global namespace ahead of "PHP\iterable" (including not listing the latter in the vote). One voter had ranked neither option. This vote will influence the name choice for the RFC https://wiki.php.net/rfc/any_all_on_iterable This is a ranked-choice poll (following [[https://en.wikipedia.org/wiki/Single_transferable_vote#Example|STV]]) between the naming alternatives. With STV you SHOULD rank all the choices in order (but are not required to). Don't pick the same option more than once, as that invalidates your vote. **Clicking on vote will only submit your vote for the above question, not for all of them.** You may want to write down your choices to avoid losing track of them. To quickly return to the vote section, click on "Vote" in the Table of Contents on the right. * no namespace: iterable_any()/iterable_all() * iter\ (conflicts with [[https://github.com/nikic/iter|nikic/iter]] but was among Nikita's suggestions) * iterable\ (see [[#arguments_foragainst_iterable_in_namespace|section on iterable\ ]] , also suggested by Nikita) * PHP\ (see [[#arguments_for_php|Arguments for PHP\]]) * PHP\Spl\ (see [[#arguments_for_php|Arguments for PHP\]]) * PHP\iter\ (see [[#arguments_for_php|Arguments for PHP\]]) * PHP\iterable\ (see [[#arguments_for_php|Arguments for PHP\]]) * Ext\Spl, as one interpretation of https://wiki.php.net/rfc/php_namespace_policy . Also see [[#arguments_for_php|Arguments for PHP\]] for PHP\Spl since this the new functions would be built in. * Spl\ (see [[#arguments_for_spl|Arguments for Spl\ ]] - suggested by Levi Morrison) * Spl\iter\ * Spl\iterable\ First choice: * iterable_any() and iterable_all() * iter\ * iterable\ * PHP\ * PHP\Spl\ * PHP\iter\ * PHP\iterable\ * Ext\Spl\ * Spl\ * Spl\iter\ * Spl\iterable\ Second choice: * iterable_any() and iterable_all() * iter\ * iterable\ * PHP\ * PHP\Spl\ * PHP\iter\ * PHP\iterable\ * Ext\Spl\ * Spl\ * Spl\iter\ * Spl\iterable\ Third choice: * iterable_any() and iterable_all() * iter\ * iterable\ * PHP\ * PHP\Spl\ * PHP\iter\ * PHP\iterable\ * Ext\Spl\ * Spl\ * Spl\iter\ * Spl\iterable\ Fourth choice: * iterable_any() and iterable_all() * iter\ * iterable\ * PHP\ * PHP\Spl\ * PHP\iter\ * PHP\iterable\ * Ext\Spl\ * Spl\ * Spl\iter\ * Spl\iterable\ Fifth choice: * iterable_any() and iterable_all() * iter\ * iterable\ * PHP\ * PHP\Spl\ * PHP\iter\ * PHP\iterable\ * Ext\Spl\ * Spl\ * Spl\iter\ * Spl\iterable\ Sixth choice: * iterable_any() and iterable_all() * iter\ * iterable\ * PHP\ * PHP\Spl\ * PHP\iter\ * PHP\iterable\ * Ext\Spl\ * Spl\ * Spl\iter\ * Spl\iterable\ Seventh choice: * iterable_any() and iterable_all() * iter\ * iterable\ * PHP\ * PHP\Spl\ * PHP\iter\ * PHP\iterable\ * Ext\Spl\ * Spl\ * Spl\iter\ * Spl\iterable\ Eighth choice: * iterable_any() and iterable_all() * iter\ * iterable\ * PHP\ * PHP\Spl\ * PHP\iter\ * PHP\iterable\ * Ext\Spl\ * Spl\ * Spl\iter\ * Spl\iterable\ Ninth choice: * iterable_any() and iterable_all() * iter\ * iterable\ * PHP\ * PHP\Spl\ * PHP\iter\ * PHP\iterable\ * Ext\Spl\ * Spl\ * Spl\iter\ * Spl\iterable\ Tenth choice: * iterable_any() and iterable_all() * iter\ * iterable\ * PHP\ * PHP\Spl\ * PHP\iter\ * PHP\iterable\ * Ext\Spl\ * Spl\ * Spl\iter\ * Spl\iterable\ Eleventh choice: * iterable_any() and iterable_all() * iter\ * iterable\ * PHP\ * PHP\Spl\ * PHP\iter\ * PHP\iterable\ * Ext\Spl\ * Spl\ * Spl\iter\ * Spl\iterable\ ===== What the functions do ===== See https://wiki.php.net/rfc/any_all_on_iterable /** Determines whether any element of the iterable satisfies the predicate. */ function(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(iterable $input, ?callable $callback = null) { foreach ($input as $v) { if (!($callback !== null ? $callback($v) : $v)) { return false; } } return true; }; ===== Future extension of any/all ===== It is possible that functionality to check if any/all keys satisfy a predicate, or any/all combinations of entries add a predicate. If this gets added, there are two possibilities - Add ''*any_key'' for keys, and ''*any_entry''/''*any_key_value()'' for key+value combination - Add flags to the ''*any()'' method, like ''array_filter()'' There's precedent for both choices of naming patterns, and I'm fine with either choice. Precedents include array_values() and array_keys(), as well as array_diff() and array_diff_keys(). ===== Rejected Choices ===== Using static methods instead of global functions was not considered since it would be impractical to polyfill new functions that get added in future php versions in a standard way. (e.g. ''IterUtils::all()'') ==== all() and iter_all() ==== Those were rejected in https://wiki.php.net/rfc/any_all_on_iterable_straw_poll#vote ==== Existing iterator methods ==== The prefix ''iterator_'' was not considered. In PHP, [[https://www.php.net/manual/en/class.iterator|Iterator]] and [[https://www.php.net/manual/en/class.iteratoraggregate.php|IteratorAggregate]] already exist, and classes that implement Traversable must implement either Iterator or IteratorAggregate. The name ''iterator_'' would be misleading for functions that also accept arrays. * I don't want to be in a situation where there are a large number of ''iterator_*()'' functions, where some accept ''Traversable'' and some accept ''iterable''. * I also don't want to be in a situation where some have $iterator as a named argument (since PHP 8.0, e.g. ''iterator_to_array()'') and others have $iterable. * If I was choosing names from scratch, ''*foo(iterable $iterable, ...)'' would make more sense than ''iterator_foo(iterable $iterator, ...)'' Changing other iterator functions such as ''iterator_apply()'', ''iterator_count()'', and ''iterator_to_array()'' to accept iterator instead of Traversable(Iterator and IteratorAggregate) is out of the scope of the RFC or straw poll. * My preference would be to add new ''iterable_apply()'', ''iterable_count()'', and ''iterable_to_array()'' functions instead of modifying the existing methods. This would be possible to polyfill, and it would be less likely that code developed for 8.1+ would pass arrays that would be rejected by php 8.0 and older. * Any alternative RFCs to change the behaviors of ''iterator_apply()'', ''iterator_count()'', and ''iterator_to_array()'' can be done independently of adding ''*any()'' and ''*all()''. ===== References ===== - https://wiki.php.net/rfc/any_all_on_iterable - https://wiki.php.net/rfc/any_all_on_iterable_straw_poll - https://externals.io/message/112558 "Straw poll: Naming for `*any()` and `*all()` on iterables" - https://github.com/php/php-src/pull/6053 ===== Changelog ===== 0.2: Switch to a vote on multiple namespaces