rfc:pipe-operator-v2

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
rfc:pipe-operator-v2 [2021/07/04 01:04] crellrfc:pipe-operator-v2 [2021/07/20 15:34] (current) – Close vote. crell
Line 3: Line 3:
   * Date: 2020-04-20   * Date: 2020-04-20
   * Author: Larry Garfield <larry@garfieldtech.com>   * Author: Larry Garfield <larry@garfieldtech.com>
-  * Status: Under Discussion+  * Status: Declined
   * First Published at: http://wiki.php.net/rfc/pipe-operator-v2   * First Published at: http://wiki.php.net/rfc/pipe-operator-v2
  
Line 25: Line 25:
 </code> </code>
  
-That, however, is still rather verbose and requires defining intermediary variables, and thus either coming up with  names for them or using generic placeholder names like `$x`.  The result is still error prone and it is possible to get confused by the variable names without realizing it.  It's also not intuitively obvious that what's happening is passing the output of one function to the next.+That, however, is still rather verbose and requires defining intermediary variables, and thus either coming up with  names for them or using generic placeholder names like ''$x''.  The result is still error prone and it is possible to get confused by the variable names without realizing it.  It's also not intuitively obvious that what's happening is passing the output of one function to the next.
  
 In OOP, it's common to answer "well use a fluent interface," which might look like this: In OOP, it's common to answer "well use a fluent interface," which might look like this:
Line 140: Line 140:
 ===== Additional semantics ===== ===== Additional semantics =====
  
-Functions that accept their first parameter by reference are supportedand will behave exactly as if they were called in the normal "inside out" fashion.  Howeverunless they return a value as well they are not of much use.+Functions that accept their first parameter by reference are allowedas are functions that return by reference.  They will behave semantically the same as if they were passed a variable by reference or returned a variable by reference via a "normal" call.  In practicehowever, reference variables are of little use in pipes so this is more of a design artifact than design intent.
  
 When evaluating a pipe, the left-hand side is fully evaluated first, then the right-hand side, then the right-hand side is invoked using the left-hand side.  That is, evaluation is strictly left-to-right. When evaluating a pipe, the left-hand side is fully evaluated first, then the right-hand side, then the right-hand side is invoked using the left-hand side.  That is, evaluation is strictly left-to-right.
Line 196: Line 196:
 function getLineCount(string $directory, string $ext): int { function getLineCount(string $directory, string $ext): int {
   return new RecursiveDirectoryIterator('.')   return new RecursiveDirectoryIterator('.')
-    |> new RecursiveIteratorIterator(?)+    |> fn($x) => new RecursiveIteratorIterator($x)
     |> itfilter(fn ($file) => $file->getExtension() == $ext)     |> itfilter(fn ($file) => $file->getExtension() == $ext)
     |> itmap(nonEmptyLines(...))     |> itmap(nonEmptyLines(...))
Line 208: Line 208:
 ===== Prior art ===== ===== Prior art =====
  
-A previous RFC, [[https://wiki.php.net/rfc/pipe-operator|Pipe Operator v1]] from 2016 by Sara Golemon and Marcelo Camargo, proposed similar functionality.  Its primary difference was to model on Hack, which allowed an arbitrary expression on the right-hand side and introduced a new `$$magic variable as a placeholder for the left-hand side.  While promising, the v2 authors concluded that short-lambdas made a custom one-off syntax unnecessary.  The semantics proposed here are more consistent with most languages that offer a pipe operator.+A previous RFC, [[https://wiki.php.net/rfc/pipe-operator|Pipe Operator v1]] from 2016 by Sara Golemon and Marcelo Camargo, proposed similar functionality.  Its primary difference was to model on Hack, which allowed an arbitrary expression on the right-hand side and introduced a new ''$$'' magic variable as a placeholder for the left-hand side.  While promising, the v2 authors concluded that short-lambdas made a custom one-off syntax unnecessary.  The semantics proposed here are more consistent with most languages that offer a pipe operator.
  
 Additionally, the comprehension-esque usage noted above would be infeasible with a non-callable right hand side. Additionally, the comprehension-esque usage noted above would be infeasible with a non-callable right hand side.
Line 218: Line 218:
 Multiple user-space libraries exist in PHP that attempt to replicate pipe-like behavior.  All are clunky and complex by necessity compared to a native solution, but demonstrate that there is desire for pipeline behavior. Multiple user-space libraries exist in PHP that attempt to replicate pipe-like behavior.  All are clunky and complex by necessity compared to a native solution, but demonstrate that there is desire for pipeline behavior.
  
-  * The PHP League has a [[https://pipeline.thephpleague.com/|Pipeline]] library that encourages wrapping all functions into classes with an __invoke() method to allow them to be referenced, and using a ''->pipe()'' call for each step.+  * The PHP League has a [[https://pipeline.thephpleague.com/|Pipeline]] library that encourages wrapping all functions into classes with an ''%%__invoke()%%'' method to allow them to be referenced, and using a ''->pipe()'' call for each step.
   * Laravel includes a [[https://github.com/illuminate/pipeline|Illuminate/Pipeline]] package that has an [[https://agoalofalife.medium.com/pipeline-and-php-d9bb0a6370ca|even more cumbersome syntax]].   * Laravel includes a [[https://github.com/illuminate/pipeline|Illuminate/Pipeline]] package that has an [[https://agoalofalife.medium.com/pipeline-and-php-d9bb0a6370ca|even more cumbersome syntax]].
 +  * The [[https://github.com/azjezz/psl|PHP Standard Library]] (PSL) library includes a [[https://github.com/azjezz/psl/blob/1.8.x/src/Psl/Fun/pipe.php|pipe function]], though it is more of a function concatenation operation.
 +  * [[https://github.com/sebastiaanluca/php-pipe-operator|Sebastiaan Luca]] has a pipe library that works through abuse of the ''%%__call%%'' method.  It only works for named functions, I believe, not for arbitrary callables.
   * Various blogs speak of "the Pipeline Pattern" ([[https://medium.com/@aaronweatherall/the-pipeline-pattern-for-fun-and-profit-9b5f43a98130|for example]])   * Various blogs speak of "the Pipeline Pattern" ([[https://medium.com/@aaronweatherall/the-pipeline-pattern-for-fun-and-profit-9b5f43a98130|for example]])
  
Line 230: Line 232:
 ==== Hacklang ==== ==== Hacklang ====
  
-Hack has [[https://docs.hhvm.com/hack/expressions-and-operators/pipe|very similar functionality]], also using the `|>operator.  However, in Hack the operator's right-hand side is an arbitrary expression in which a special placeholder, `$$is used to indicate where the left-hand side should be injected.  Effectively it becomes a one-off form of partial application.+Hack has [[https://docs.hhvm.com/hack/expressions-and-operators/pipe|very similar functionality]], also using the ''|>'' operator.  However, in Hack the operator's right-hand side is an arbitrary expression in which a special placeholder, ''$$'' is used to indicate where the left-hand side should be injected.  Effectively it becomes a one-off form of partial application.
  
 That is atypical among languages with such functionality and introduces additional questions about what sigil to use and other implementation details.  The RFC authors believe that a fully-fleshed out partial function application syntax (in a separate RFC) is superior, and integrates cleanly with this RFC. That is atypical among languages with such functionality and introduces additional questions about what sigil to use and other implementation details.  The RFC authors believe that a fully-fleshed out partial function application syntax (in a separate RFC) is superior, and integrates cleanly with this RFC.
Line 238: Line 240:
 ==== Haskell ==== ==== Haskell ====
  
-Haskell has a [[https://wiki.haskell.org/Function_composition|function concatenation operator]], `.`.  However, its semantics are backwards.  `reverse . sortis equivalent to `reverse(sort())`, not to `sort(reverse())`.  It also returns a new composed callable rather than invoking immediately.+Haskell has a [[https://wiki.haskell.org/Function_composition|function concatenation operator]], ''.''.  However, its semantics are backwards.  ''reverse . sort'' is equivalent to ''reverse(sort())'', not to ''sort(reverse())''  It also returns a new composed callable rather than invoking immediately.
  
-The inverse ordering is more difficult to reason about, and unfamiliar for PHP developers.  The `.operator itself would also cause confusion with the string concatenation operator, especially as strings can be callables.  That is:+The inverse ordering is more difficult to reason about, and unfamiliar for PHP developers.  The ''.'' operator itself would also cause confusion with the string concatenation operator, especially as strings can be callables.  That is:
  
 <code php> <code php>
Line 246: Line 248:
 </code> </code>
  
-Could be interpreted as evaluating to "hellostrlen" or to int 5.  For that reason the `.operator is not feasible.+Could be interpreted as evaluating to "hellostrlen" or to int 5.  For that reason the ''.'' operator is not feasible.
  
 Haskell also has a ''&'' operator, which is the "reverse application operator."  Its semantics are essentially the same as described here, including listing functions "forward" rather than backward. Haskell also has a ''&'' operator, which is the "reverse application operator."  Its semantics are essentially the same as described here, including listing functions "forward" rather than backward.
Line 252: Line 254:
 ==== F# ==== ==== F# ====
  
-F# has no less than four function composition operators: Pipe forward `|>`, Pipe back `<|`, Compose forward `>>and Compose back `<<`.  The two pipe operators apply a value to a function, while the composer operator concatenates two functions to produce a new function that is the composition of the specified functions.  The forward and back variants allow you to put the callable on either the left or right-hand side.+F# has no less than four function composition operators: Pipe forward ''|>'', Pipe back ''<|'', Compose forward ''>>'' and Compose back ''<<''.  The two pipe operators apply a value to a function, while the composer operator concatenates two functions to produce a new function that is the composition of the specified functions.  The forward and back variants allow you to put the callable on either the left or right-hand side.
  
 The author decided that supporting both forward and back versions was too confusing.  Additionally, a concatenation operator is unnecessary since users can simply form a short-lambda closure themselves. The author decided that supporting both forward and back versions was too confusing.  Additionally, a concatenation operator is unnecessary since users can simply form a short-lambda closure themselves.
Line 260: Line 262:
 ==== Elixir ==== ==== Elixir ====
  
-[[https://elixirschool.com/en/lessons/basics/pipe-operator/|Elixir has a pipe operator]], `|>`, using essentially the same semantics as described here.+[[https://elixirschool.com/en/lessons/basics/pipe-operator/|Elixir has a pipe operator]], ''|>'', using essentially the same semantics as described here.
  
 ==== Ruby ==== ==== Ruby ====
Line 268: Line 270:
 ==== Javascript ==== ==== Javascript ====
  
-A pipeline operator `|>has been [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Pipeline_operator|proposed for Javascript]].  As of this writing it is still in early stages and no implementations support it, but it may get accepted in the future.  The semantics are essentially the same as described here.+A pipeline operator ''|>'' has been [[https://github.com/tc39/proposal-pipeline-operator/wiki|proposed for Javascript]].  As of this writing it is still in early stages and no implementations support it, but it may get accepted in the future.  The semantics are essentially the same as described here.
  
 ==== OCaml ==== ==== OCaml ====
Line 282: Line 284:
 * Iterable right-hand side.  The pipe operator as presented here can only be used in a hard-coded fashion.  A possible extension is to support an iterable of callables on the right-hand side, allowing for a runtime-defined pipeline. * Iterable right-hand side.  The pipe operator as presented here can only be used in a hard-coded fashion.  A possible extension is to support an iterable of callables on the right-hand side, allowing for a runtime-defined pipeline.
  
-* A `__bindmethod or similar on objects.  If implemented by an object on the left-hand side, the right-hand side would be passed to that method to invoke as it sees fit.  Effectively this would be operator overloading, which could be part of a second attempt at full operator overloading or a one-off magic method.  It could also be implemented as a separate operator instead, for clarity.  Such a feature would be sufficient to support arbitrary monadic behavior in PHP in a type-friendly way.+* A ''%%__bind%%'' method or similar on objects.  If implemented by an object on the left-hand side, the right-hand side would be passed to that method to invoke as it sees fit.  Effectively this would be operator overloading, which could be part of a second attempt at full operator overloading or a one-off magic method.  It could also be implemented as a separate operator instead, for clarity.  Such a feature would be sufficient to support arbitrary monadic behavior in PHP in a type-friendly way.
  
 These options are mentioned here for completeness and to give an indication of what is possible, but are *not* in scope and are *not* part of this RFC at this time. These options are mentioned here for completeness and to give an indication of what is possible, but are *not* in scope and are *not* part of this RFC at this time.
Line 297: Line 299:
  
 Adopt the Pipe Operator yes/no?  Requires a 2/3 majority. Adopt the Pipe Operator yes/no?  Requires a 2/3 majority.
 +
 +<doodle title="Pipe Operator" auth="crell" voteType="single" closed="true">
 +   * Yes
 +   * No
 +</doodle>
 +
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
rfc/pipe-operator-v2.1625360699.txt.gz · Last modified: 2021/07/04 01:04 by crell