rfc:pipe-operator
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revisionNext revisionBoth sides next revision | ||
rfc:pipe-operator [2016/04/29 19:56] – created pollita | rfc:pipe-operator [2017/09/06 02:36] – Small typo fix haskellcamargo | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Pipe Operator ====== | ====== PHP RFC: Pipe Operator ====== | ||
- | * Version: 0.1 | + | * Version: 0.2 |
* Date: 2016-04-29 | * Date: 2016-04-29 | ||
* Author: Sara Golemon < | * Author: Sara Golemon < | ||
Line 8: | Line 8: | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | Complex nested expressions can become progressively difficult to read the deeper they go with function returns leading | + | A common PHP OOP pattern is the use of method chaining, or what is also known as " |
+ | |||
+ | For example, the following shows a SQL query expression built out of component pieces, then executed: | ||
+ | |||
+ | $rs = $db | ||
+ | -> | ||
+ | -> | ||
+ | -> | ||
+ | -> | ||
+ | -> | ||
+ | |||
+ | This works well enough for OOP classes | ||
+ | |||
+ | While decomposing these expressions to make use of multiple variables is an option, this can lead to reduced readability, | ||
+ | |||
+ | $config = loadConfig(); | ||
+ | $dic = buildDic($config); | ||
+ | $app = getApp($dic); | ||
+ | $router = getRouter($app); | ||
+ | $dispatcher = getDispatcher($router, | ||
+ | $logic = dispatchBusinessLogic($dispatcher, | ||
+ | $render = renderResponse($logic); | ||
+ | $psr7 = buildPsr7Response($render); | ||
+ | $response =emit($psr7); | ||
+ | |||
+ | Or: | ||
+ | |||
+ | $x = loadConfig(); | ||
+ | $x = buildDic($x); | ||
+ | $x = getApp($x); | ||
+ | $x = getRouter($x); | ||
+ | $x = getDispatcher($x, | ||
+ | $x = dispatchBusinessLogic($x, | ||
+ | $x = renderResponse($x); | ||
+ | $x = buildPsr7Response($x); | ||
+ | $response =emit($x); | ||
+ | |||
+ | These sorts of chains could also, conceivably, | ||
+ | |||
+ | $dispatcher = getDispatcher(getRouter(getApp(buildDic(loadConfig()))), | ||
+ | $response = emit(buildPsr7Response(renderResponse(dispatchBusinessLogic($dispatcher, | ||
+ | |||
+ | This RFC aims to improve code readability by bringing fluent | ||
===== Proposal ===== | ===== Proposal ===== | ||
- | Introduce the "Pipe Operator" | + | Introduce the "Pipe Operator" |
This feature is being culled from HackLang 3.13 and the manual page for it may be referenced at: https:// | This feature is being culled from HackLang 3.13 and the manual page for it may be referenced at: https:// | ||
- | As an example, consider the following real block of code I wrote while creating a test importer (to migrate HHVM format tests into PHPT format): | + | ==== PSR7 Example ==== |
+ | |||
+ | Here's the equivalent chain of function calls as demonstrated in the intro section above: | ||
+ | |||
+ | $response = loadConfig() | ||
+ | |> buildDic($$) | ||
+ | |> getApp($$) | ||
+ | |> getRouter($$) | ||
+ | |> getDispatcher($$, | ||
+ | |> dispatchBusinessLogic($$, | ||
+ | |> renderResponse($$) | ||
+ | |> buildPsr7Response($$) | ||
+ | |> emit($$); | ||
+ | |||
+ | ==== File collection Example ==== | ||
+ | |||
+ | As an example, consider the following real block of code I wrote while creating a test importer (to migrate HHVM format tests into PHPT format). Please try not to get hung up into whether or not it's " | ||
$ret = | $ret = | ||
Line 23: | Line 81: | ||
getFileArg( | getFileArg( | ||
array_map( | array_map( | ||
- | function ($x) { return $arg . '/' | + | function ($x) use ($arg) { return $arg . '/' |
array_filter( | array_filter( | ||
scandir($arg), | scandir($arg), | ||
Line 39: | Line 97: | ||
$ret = scandir($arg) | $ret = scandir($arg) | ||
|> array_filter($$, | |> array_filter($$, | ||
- | |> array_map(function ($x) { return $arg . '/' | + | |> array_map(function ($x) use ($arg) { return $arg . '/' |
|> getFileArg($$) | |> getFileArg($$) | ||
|> array_merge($ret, | |> array_merge($ret, | ||
This clearly, and unambiguously shows `scandir()` as the initial source of data, that it goes through an `array_filter` to avoid recursion, an `array_map` to requalify the paths, some local function, and finally a merge to combine the result with a collector variable. | This clearly, and unambiguously shows `scandir()` as the initial source of data, that it goes through an `array_filter` to avoid recursion, an `array_map` to requalify the paths, some local function, and finally a merge to combine the result with a collector variable. | ||
+ | |||
+ | ==== FBShipIt Example ==== | ||
+ | |||
+ | Also consider [[https:// | ||
+ | |||
+ | return $changeset | ||
+ | |> self:: | ||
+ | |> self:: | ||
+ | $$, | ||
+ | $config[' | ||
+ | ) | ||
+ | |> self:: | ||
+ | |> self:: | ||
+ | |> self:: | ||
+ | |> self:: | ||
+ | |> ShipItUserFilters:: | ||
+ | $$, | ||
+ | FBToGitHubUserInfo:: | ||
+ | ) | ||
+ | |> self:: | ||
+ | $$, | ||
+ | $config[' | ||
+ | ?? self:: | ||
+ | ) | ||
+ | |> self:: | ||
+ | |> self:: | ||
+ | |> self:: | ||
+ | |||
+ | This presents every step taken by the common filter chain in an easy to follow list of actions. | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
Line 55: | Line 142: | ||
// Actual: Use of $$ outside of a pipe expression | // Actual: Use of $$ outside of a pipe expression | ||
- | This particular quirk of the parser (allowing comments in the middle of a variable-variable-brace-expression is doubtlessly a rare occurance | + | This particular quirk of the parser (allowing comments in the middle of a variable-variable-brace-expression) is doubtlessly a rare occurrence |
Potential resolutions: | Potential resolutions: | ||
* Use a less-ambiguous token. | * Use a less-ambiguous token. | ||
* Get very creative in the parser. | * Get very creative in the parser. | ||
+ | |||
+ | Note that HHVM does not handle this case either. | ||
+ | |||
+ | **Update:** HackLang is normally supposed to disallow variable-variables, | ||
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
- | 7.Next | + | 7.2 |
===== Open Issues ===== | ===== Open Issues ===== | ||
Line 69: | Line 160: | ||
===== Future Scope ===== | ===== Future Scope ===== | ||
The current proposal limits use of the `$$` to a single replacement per expression. | The current proposal limits use of the `$$` to a single replacement per expression. | ||
+ | |||
+ | ===== Third-party Arguments ===== | ||
+ | |||
+ | Informal Twitter poll (821 respondents) results: https:// | ||
+ | |||
+ | * 62% "Love It" | ||
+ | * 24% " | ||
+ | * 14% "Hate It" | ||
+ | |||
+ | ==== In favor ==== | ||
+ | |||
+ | * Produces cleaner, more readable code | ||
+ | * Doesn' | ||
+ | |||
+ | ==== Against ==== | ||
+ | |||
+ | * The new tokens are inobvious and difficult to google for | ||
+ | * Pipe chaining in other languages follows different rules \\ (e.g. implicit first arg, rather than explicit placeholder) | ||
+ | * Potentially confusable with variable-variables | ||
+ | * No opportunity for error catching/ | ||
+ | * Can be implemented using intermediate variables | ||
===== Proposed Voting Choices ===== | ===== Proposed Voting Choices ===== | ||
- | Adopt the PIpe Operator yes/ | + | Adopt the Pipe Operator yes/ |
===== Patches and Tests ===== | ===== Patches and Tests ===== |
rfc/pipe-operator.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1