rfc:iteration-tools

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
rfc:iteration-tools [2008/10/29 02:00] amenthesrfc:iteration-tools [2008/11/03 15:05] – typos amenthes
Line 1: Line 1:
 ====== Request for Comments: Iteration tools in PHP ====== ====== Request for Comments: Iteration tools in PHP ======
   * Version: 1.0   * Version: 1.0
-  * Date: 2008-10-29+  * Date: 2008-11-03
   * Author: Ionut Gabriel Stan <ionut.dot.g.dot.stan.at.gmail.com>   * Author: Ionut Gabriel Stan <ionut.dot.g.dot.stan.at.gmail.com>
-  * Status: Under Discussion+  * Status: In the works
   * First Published at: http://wiki.php.net/rfc/iteration-tools   * First Published at: http://wiki.php.net/rfc/iteration-tools
  
 This RFC proposes a series of functions or classes This RFC proposes a series of functions or classes
-to facilitate easy processing of data sets represented as either arrays or Traversables. The assumed PHP version is 5.3 because of the new lambda structures needed for this proposal.+to facilitate easy processing of data sets represented as either arrays or Traversables. The assumed PHP version is 5.3 because of the new lambda structures that inspired this proposal.
  
 ===== Introduction ===== ===== Introduction =====
  
-Most of the programs we write, invariable of the programming language we use, have as their purpose data processing. While this data can be represented in various formats it is as true that quite often this data comes grouped as sets of data. For example it is a common situation to issue a database query which resturns a result set or just as well we might need to read the contents of a directory or the structure of an XML document. Oblivious of the data source these result sets are represented in PHP in three ways:+Most of the programs we write, invariable of the programming language we use, have as their purpose data processing. While this data can be represented in various formats it is as true that quite often this data comes grouped as sets of data. For example it is a common situation to issue a database query which returns a result set or just as well we might need to read the contents of a directory or the structure of an XML document. Oblivious of the data source these result sets are represented in PHP in three ways:
     * array. Ex: scandir()     * array. Ex: scandir()
     * Traversable. Ex: DirectoryIterator     * Traversable. Ex: DirectoryIterator
Line 21: Line 21:
     * foreach     * foreach
  
-Given that this is such recurrent situation and conforming to the DRY principle but also in total respect with common sense an abstraction is required.+Depending on the task at hand the processing involved inside these loops may be ridiculously easy or painfully hard. With time, the more you do this the more you realize there'pattern emerging out there and there must be some "tools" to ease our job.
  
  
-==== Why do we need RFCs? ==== 
  
-Depending on the task at hand the processing involved inside these loops may be ridiculously easy or painfully hard. With timethe more you do this the more you realize there's a pattern emerging out there. Thankfully, these patterns were observed almost fourty years ago. These people found out that iterative processes can be abstracted away in a handful of functions.+==== Why do we need tools for iteration ==== 
 + 
 +Given that iteration is such a recurrent situation and conforming to the DRY principlebut also in total respect with common sense, an abstraction is required. Thankfully, patterns regarding iteration were observed almost forty years ago by some very smart people. These people found out that iterative processes can be abstracted away in a handful of functions.
 For example: For example:
     * some of the functions modify data in the set     * some of the functions modify data in the set
Line 32: Line 33:
     * some use all the data in the result to create a combined result     * some use all the data in the result to create a combined result
  
-...and the list may go on with a few other abstracted use cases. +The list may go on with a few other abstracted use cases. 
-It turns out that separating the iteration from the inner data calculations is a good thing and people came up with what they called higher order functions, that took at least two parameters, the data set to process and the *function* that did the processing (which in some of the cases were "unnamed" functions or lambdas). Languages that did not have possibilities for higher order functions made use of their best features and provided different alternatives if any. For example in PHP we have at our disposal the following SPL classes revolving around the same idea:+ 
 +It turns out that separating the iteration from the inner data calculations is a good thing and people came up with some higher order functions, that took at least two parameters, the data set to be processed and the *function* that did the processing (which in some of the cases were "unnamed" functions or lambdas). Languages that did not have possibilities for higher order functions made use of their best features and provided different alternatives if any. For example in PHP we have at our disposal the following SPL classes revolving around the same idea:
     * FilterIterator     * FilterIterator
     * RecursiveFilterIterator     * RecursiveFilterIterator
Line 39: Line 41:
     * SearchIterator     * SearchIterator
  
 +====Shortcomings of current tools====
 While these classes do their job they have some shortcomings: While these classes do their job they have some shortcomings:
-    * they only iterate over Traversables. Supporting arrays would be nice +    * They only iterate over Traversables. Supporting arrays would be nice (Indeed, we have array_map(), array_walk() and some other similar functions for array processing, yet, I think we should unify these similar tools. Not to mention that array_* functions do not adhere to some param order standard. 
-    * FilterIterator, RecursiveFilterIterator and SearchIterator require declaration of an aditional classes that extend them +    * FilterIterator, RecursiveFilterIterator and SearchIterator require declaration of an additional classes that extend them 
-    * Although not a shortcoming, we need a few more abstraction+    * CallbackFilterIterator, although I haven't had the chance to use it, it misses one important thing. It'not passing the iterator itself as third parameter to the callback.
  
-What I'm proposing is introduction in the language the following functions, which are similar to those existing in JavaScript 1.8: +What I'm proposing is introducing in the language of the following functions, which are similar to the Array methods that exist in JavaScript 1.8: 
-    * map +    * map() 
-    * forEach +    * walk() - this is forEach() in JavaScript 
-    * reduce (an alternative name in some languages is fold) +    * reduce() - in some languages this is fold(
-    * reduceRight (an alternative name in some languages is foldr) +    * reduceRight() - in some languages this is foldr(
-    * filter +    * filter() 
-    * some +    * some() 
-    * every+    * every()
  
-Pages in the Mozilla Developer Center wiki documenting these functions can be found here:+===== Common Misconceptions ===== 
 + 
 +None that I know of yet. 
 + 
 +===== Proposal and Patch ===== 
 + 
 + 
 +Pages from Mozilla Developer Center wiki documenting these kind of functions can be found here:
     * https://developer.mozilla.org/en/new_in_javascript_1.6#section_2     * https://developer.mozilla.org/en/new_in_javascript_1.6#section_2
     * https://developer.mozilla.org/en/new_in_javascript_1.8#section_4     * https://developer.mozilla.org/en/new_in_javascript_1.8#section_4
  
-Some documentation about these functionsmost of it pasted from MDC modified where necessary because of PHP related aspects:+Some PHP function signatures, mostly identical to the JavaScript versions, modified where necessary because of PHP related aspects:
  
 ===map()=== ===map()===
     * Signature: array|Iterator map(array|Traversable iter, callback callback)     * Signature: array|Iterator map(array|Traversable iter, callback callback)
-    * Description: runs a function on every item in the array|Traversable and returns the results in an array|Iterator+    * Description: runs a function on every item in the array|Traversable and returns the results into an array or Iterator, depending on the first param type
     * Callback signature: mixed callback(mixed value, mixed key, array|Traversable iter)     * Callback signature: mixed callback(mixed value, mixed key, array|Traversable iter)
  
-===forEach()=== +===walk()=== 
-    * Signature: void forEach(array|Traversable iter, callback callback)+    * This is named forEach() inside JavaScript but that's not possible inside PHP. Anyway, we already have an array_walk() function so this name might be actually a good choice 
 +    * Signature: void walk(array|Traversable iter, callback callback)
     * Description: runs a function on every item in the array|Traversable and returns nothing (just like a foreach construct with scope)     * Description: runs a function on every item in the array|Traversable and returns nothing (just like a foreach construct with scope)
     * Callback signature: void callback(mixed value, mixed key, array|Traversable iter)     * Callback signature: void callback(mixed value, mixed key, array|Traversable iter)
 +
 +===walkRecursive()===
 +    * Should there be such a function?
  
 ===reduce()=== ===reduce()===
Line 78: Line 91:
  
 ===reduceRight()=== ===reduceRight()===
-    Same as reduce() but in reverse.+    Same as reduce() but in reverse.
  
 ===filter()=== ===filter()===
Line 95: Line 108:
     * Callback signature: bool callback(mixed value, mixed key, array|Traversable iter)     * Callback signature: bool callback(mixed value, mixed key, array|Traversable iter)
  
 +Although the above tools were listed as functions, as they don't do that much, they might just as well be class constructors (honestly I don't like this approach). I thought functions could do just fine because of the new namespace support that's why I represented them as such.
  
 +Additionally, one thing I haven't represented in the above signatures is that an additional argument may be passed to the callback function representing an iteration counter.
  
 ==== Use cases ==== ==== Use cases ====
- +This an example PHP script for reading files with .php extension from a certain directory.
-This an example of a piece of code that reads all PHP files from a certain directory.+
  
 <code php> <code php>
 <?php <?php
  
-// 1.1 how could be done right now -------------------------------------------------+// 1.1 How it could be done right now -------------------------------------------------
 class OnlyPHPFiles extends FilterIterator { class OnlyPHPFiles extends FilterIterator {
     public function accept() {     public function accept() {
Line 117: Line 131:
 } }
  
-// 1.2 or with the CallbackFilterIterator which I don't know with which PHP+// 1.2 Using CallbackFilterIterator which I don't know with which PHP
 // version it will be shipped // version it will be shipped
 $dirs = new CallbackFilterIterator(new DirectoryIterator(__DIR__), function($value) { $dirs = new CallbackFilterIterator(new DirectoryIterator(__DIR__), function($value) {
Line 125: Line 139:
  
  
-// 2.1 how could be done right now with my proposal ------------------------------------+// 2.1 How it could be done with my proposal --------------------------------------
 $dirs = filter(new DirectoryIterator(__DIR__), function($current, $key, $iterator) { $dirs = filter(new DirectoryIterator(__DIR__), function($current, $key, $iterator) {
     $ext = strtolower(pathinfo($this->getRealPath(), PATHINFO_EXTENSION));     $ext = strtolower(pathinfo($this->getRealPath(), PATHINFO_EXTENSION));
Line 131: Line 145:
 }); });
  
-?> 
 </code> </code>
  
-While the 1.2 example is very similar to 2.1 it differs from it in that it's not passing the iterator to the callback function. Furthermore it does more than it should do. For example, it's not only filtering the elements of the iterator into a new iterator, but it MAY also change those values. In my proposal, the function that changes values is map() which... maps a certain value to another depending on the callback function. filter() only keeps items that validate agains certain criteria inside the callback function.+While the 1.2 example is very similar to 2.1 it differs from it in that it's not passing the iterator to the callback function and, of course, the fact that I use a function instead of an objectAnother difference is that present implementation of CallbackFilterIterator (as documented on http://www.php.net/~helly/php/ext/spl/) may also be used as a virtual CallbackMapIterator. For example, it's not only filtering the elements of the iterator into a new iterator, but it MAY also change those values. In my proposal, the function that changes values is map() which translates a certain value to another depending on the callback function. filter() only keeps items that validate against certain criteria inside the callback function. I believe a clear distinction between these features must be reflected in the API, thus two different functions in my proposal.
  
 +=====Some additional benefits=====
 +As you have seen, my proposal includes a function called walk() which does exactly the same thing as a foreach construct. The real useful thing this function provides is the ability to mimic scope inside a foreach block. So, for example:
  
 +<code php>
 +foreach ($iter as $elem) {
 +    // everything inside this foreach block is in the global space
 +}
  
 +// whereas
  
 +$global_var = 'foo';
  
-===== Common Misconceptions =====+walk($iter, function($elem) use ($global_var) { 
 +    // this is not the global space 
 +    // but we may still use variables from the global space 
 +    // by "use"-ing them 
 +});
  
-RFCs do not in any way replace discussions on the mailing list.+</code>
  
-===== Proposal and Patch =====+It would have been an advantage if our lambdas had been self-executing, just like in JavaScript, but they aren't:
  
-Nothing needs to be patched hereJust use this template at your discretion.+<code php> 
 +// as far as I know this doesn't work in PHP 5.
 +foreach ($iter as $elem) { 
 +    (function($elem) use ($global_var) { 
 +        // not global scope 
 +    })(); 
 +
 +</code>
  
-==== Rejected Features ====+===== Rejected Features =====
  
-Automated voting system. +None for the moment
- +
-==== More about RFCs ==== +
- +
-http://en.wikipedia.org/wiki/Request_for_Comments +
- +
-===== Changelog =====+
  
 +===== Similar implementations =====
  
 +JavaScript 1.8:
 +    * https://developer.mozilla.org/en/new_in_javascript_1.6#section_2
 +    * https://developer.mozilla.org/en/new_in_javascript_1.8#section_4
  
 +Python:
 +    * http://www.python.org/doc/2.5.2/tut/node7.html#SECTION007130000000000000000
 +    * http://www.python.org/doc/2.5.2/lib/itertools-functions.html (this Python package differs quite much from what I have proposed, nevertheless we may extract other useful things from there too)
  
rfc/iteration-tools.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1