[RFC] Function pipe operator

php.internals

Larry Garfield

6 years ago
Hello fine people of Internals! I've been commenting on other RFCs enough lately that I should probably put myself through the wringer, too. I therefore offer this RFC to add a function pipe operator, as seen in a number of other languages: https://wiki.php.net/rfc/pipe-operator-v2 A patch is available here: https://github.com/php/php-src/pull/5425 Although I have one or two bits of it that I could use feedback on. (Let me know over on GitHub.) It's deliberately a very small, lightweight, and targeted change. The Future Work section suggests possible additions for later, but this is as much as my meager skills can manage for the moment. My enormous thanks to Sammy K Powers and Levi Morrison for their copious hand-holding in my first foray into php-src. *dons flame retardant suit*
-- Larry Garfield larry@garfieldtech.com

Ben Ramsey

6 years ago
> On Apr 20, 2020, at 20:38, Larry Garfield <larry@garfieldtech.com> wrote: > > Hello fine people of Internals! > > I've been commenting on other RFCs enough lately that I should probably put myself through the wringer, too. I therefore offer this RFC to add a function pipe operator, as seen in a number of other languages: > > https://wiki.php.net/rfc/pipe-operator-v2 > > A patch is available here: > > https://github.com/php/php-src/pull/5425 > > Although I have one or two bits of it that I could use feedback on. (Let me know over on GitHub.) > > It's deliberately a very small, lightweight, and targeted change. The Future Work section suggests possible additions for later, but this is as much as my meager skills can manage for the moment. > > My enormous thanks to Sammy K Powers and Levi Morrison for their copious hand-holding in my first foray into php-src. > > *dons flame retardant suit*
I liked the use of expressions in the v1 proposal, but I agree with you that the use of callables simplifies the approach, and (IMO) provides a better developer experience, since I found the `$$` placeholder confusing. I’m a +1 for this. I can’t wait to use it. Cheers, Ben

Sara Golemon

6 years ago
On Mon, Apr 20, 2020 at 9:39 PM Ben Ramsey <ben@benramsey.com> wrote:
> > On Apr 20, 2020, at 20:38, Larry Garfield <larry@garfieldtech.com> > wrote: > > > > I've been commenting on other RFCs enough lately that I should probably > put myself through the wringer, too. I therefore offer this RFC to add a > function pipe operator, as seen in a number of other languages: > > > > https://wiki.php.net/rfc/pipe-operator-v2 > > >
Happy with the revival, and I do want to underline the "Future Work" section about Partial Functions. PF will make this version of pipes 100% more readable and fluent, and I want to endorse both, but I'll take one at a time. -Sara

Larry Garfield

6 years ago
Replying to a couple of people at once. On Mon, Apr 20, 2020, at 11:06 PM, Sara Golemon wrote:
> On Mon, Apr 20, 2020 at 9:39 PM Ben Ramsey <ben@benramsey.com> wrote: > > > > On Apr 20, 2020, at 20:38, Larry Garfield <larry@garfieldtech.com> > > wrote: > > > > > > I've been commenting on other RFCs enough lately that I should probably > > put myself through the wringer, too. I therefore offer this RFC to add a > > function pipe operator, as seen in a number of other languages: > > > > > > https://wiki.php.net/rfc/pipe-operator-v2 > > > > > > Happy with the revival, and I do want to underline the "Future Work" > section about Partial Functions. PF will make this version of pipes 100% > more readable and fluent, and I want to endorse both, but I'll take one at > a time. > > -Sara
On Mon, Apr 20, 2020, at 10:55 PM, Mike Schinkel wrote:
> A questions and a comment: > > 1. How will XDEBUG handle a multiline construct such as this? Will it > treat as one expression, or allow breakpoints at each "link" in the > pipe chain? > > Maybe this is a better question for Derek Rethans?
I defer to Derek. My gut feeling is that since all the executor sees is foo(bar(baz(beep($a))), it will behave the same as if you had that line and put a breakpoint on it. On Tue, Apr 21, 2020, at 4:04 AM, Marco Pivetta wrote:
> Hey Larry, > > This looks good! > > One question arises: how do exception traces look like, if they happen > mid-pipe? Is the pipe effectively attaching stack frames to the entire > thing, or is it flattening the trace?
The pipe is literally translating at the AST level to foo(bar(baz(beep($a))), so if baz() throws an exception I presume it would look the same in the stack trace. On Mon, Apr 20, 2020, at 11:20 PM, Stanislav Malyshev wrote:
> Just a small pedantry note - in a comparison section, the RFC compares > this syntax to function composition. But this is not function > composition. This is a syntax sugar for calling two functions one after > another, not operator that produces a function. It sounds pedantic but > it's rather important distinction - if |> is composition, than $foo |> > $bar is a new callable provided $foo and $bar are callable (but no > function is actually being called here!). If |> is call syntax, it's > actually the result of calling $bar($foo). > > So comparing it to function composition is a bit confusing. Otherwise it > looks OK to me, except the syntax for calling functions and methods is a > bit awkward, but it's not the problem of this RFC I imagine.
I'm not sure I follow. The only place composition is mentioned is in the F# section, where it calls out specifically that we're implementing "pipe forward" and *not* the composition operators ( >> ). Is that unclear? And a bunch of people on this topic: Mike Schinkel:
> 2. The exclusive use of callable seems problematic prior to an > inclusion a '::function' operator that can be applied to functions for > symbol resolution to function name as it will encourage more use of > strings that are not checked by PHP. Maybe we could address that first? > > Also #2 would be true for method names although how that might be > implemented is a bit more complicated.
On Tue, Apr 21, 2020, at 3:59 AM, Nikita Popov wrote:
> I like the basic idea, but don't think it makes sense to introduce the > syntax *before* we have first-class support for partial function > application. Without it, you're forced to use PHP's awkward callable > syntax, which imho makes for a regression over writing things the normal > way. > > Nikita
On Mon, Apr 20, 2020, at 11:06 PM, Sara Golemon wrote:
> Happy with the revival, and I do want to underline the "Future Work" > section about Partial Functions. PF will make this version of pipes 100% > more readable and fluent, and I want to endorse both, but I'll take one at > a time.
I agree that the pipe operator relies on having a "good" (subjective) way to reference callables, which is a weakness of PHP right now. We discussed that in depth a few months ago as well. IMO, short-lambdas in 7.4 are "good enough" to make the pipe operator useful. They're not ideal, but they're sufficient. Levi's partial application RFC would be close enough to ideal; You could reference an already-single-parameter or multi-parameter function lexically without any string parsing silliness, which means namespace resolution works, and it could potentially be optimized to avoid an extra layer of function call. It would be even better than ::function, IMO, because you can provide it with closed-over variables at the same time. It sounds like we all want that. I do not believe that should block the pipe operator, however. This is a case where two independent RFCs should be able to stand on their own but create a "sum is greater than their parts" situation, which is a good thing. If anyone has bandwidth, I'm happy to collaborate on the PF RFC as well. It's out of my solo skillset right now (frankly so was pipe until this weekend), but if someone is able to provide hand-holding/pairing I'm willing to work on that, too, either now in parallel or after pipe lands. Both should happen, but I don't think either should block the other. --Larry Garfield

Guilliam Xavier

6 years ago
On Tue, Apr 21, 2020 at 4:44 PM Larry Garfield <larry@garfieldtech.com> wrote:
> > On Mon, Apr 20, 2020, at 11:20 PM, Stanislav Malyshev wrote: > > Just a small pedantry note - in a comparison section, the RFC compares > > this syntax to function composition. But this is not function > > composition. This is a syntax sugar for calling two functions one after > > another, not operator that produces a function. It sounds pedantic but > > it's rather important distinction - if |> is composition, than $foo |> > > $bar is a new callable provided $foo and $bar are callable (but no > > function is actually being called here!). If |> is call syntax, it's > > actually the result of calling $bar($foo). > > > > So comparing it to function composition is a bit confusing. Otherwise it > > looks OK to me, except the syntax for calling functions and methods is a > > bit awkward, but it's not the problem of this RFC I imagine. > > I'm not sure I follow. The only place composition is mentioned is in the F# section, where it calls out specifically that we're implementing "pipe forward" and *not* the composition operators ( >> ). Is that unclear?
Actually it's also mentioned in the Haskell section, but as "function concatenation" (which adds to the confusion I guess). Speaking of Haskell, that reminded me of http://learnyouahaskell.com/a-fistful-of-monads#walk-the-line where the author defines a custom `-:` operator such that `x -: f` desugars to `f x` (equivalent to how `$x |> $f` would desugar to `$f($x)` with the RFC), which allows to write e.g. `xs -: sort -: reverse` instead of `reverse (sort xs)` or `(reverse . sort) xs`.
-- Guilliam Xavier

Larry Garfield

6 years ago
On Wed, Apr 22, 2020, at 3:25 AM, Guilliam Xavier wrote:
> On Tue, Apr 21, 2020 at 4:44 PM Larry Garfield <larry@garfieldtech.com> wrote: > > > > On Mon, Apr 20, 2020, at 11:20 PM, Stanislav Malyshev wrote: > > > Just a small pedantry note - in a comparison section, the RFC compares > > > this syntax to function composition. But this is not function > > > composition. This is a syntax sugar for calling two functions one after > > > another, not operator that produces a function. It sounds pedantic but > > > it's rather important distinction - if |> is composition, than $foo |> > > > $bar is a new callable provided $foo and $bar are callable (but no > > > function is actually being called here!). If |> is call syntax, it's > > > actually the result of calling $bar($foo). > > > > > > So comparing it to function composition is a bit confusing. Otherwise it > > > looks OK to me, except the syntax for calling functions and methods is a > > > bit awkward, but it's not the problem of this RFC I imagine. > > > > I'm not sure I follow. The only place composition is mentioned is in the F# section, where it calls out specifically that we're implementing "pipe forward" and *not* the composition operators ( >> ). Is that unclear? > > Actually it's also mentioned in the Haskell section, but as "function > concatenation" (which adds to the confusion I guess).
Ah, I see what you mean. I've adjusted that section to be clearer about what Haskell does.
> Speaking of Haskell, that reminded me of > http://learnyouahaskell.com/a-fistful-of-monads#walk-the-line where > the author defines a custom `-:` operator such that `x -: f` desugars > to `f x` (equivalent to how `$x |> $f` would desugar to `$f($x)` with > the RFC), which allows to write e.g. `xs -: sort -: reverse` instead > of `reverse (sort xs)` or `(reverse . sort) xs`.
If I ever actually wrote Haskell, I'd find that extremely useful. :-) --Larry Garfield

Weirdan

6 years ago
Haskell has & operator in Data.Function module which is exact equivalent of the proposed feature. Link: https://hackage.haskell.org/package/base-4.8.0.0/docs/Data-Function.html#g:2 Example: https://repl.it/repls/KindLightsalmonApplicationserver On Wed, Apr 22, 2020 at 9:09 PM Larry Garfield <larry@garfieldtech.com> wrote:
> On Wed, Apr 22, 2020, at 3:25 AM, Guilliam Xavier wrote: > > On Tue, Apr 21, 2020 at 4:44 PM Larry Garfield <larry@garfieldtech.com> > wrote: > > > > > > On Mon, Apr 20, 2020, at 11:20 PM, Stanislav Malyshev wrote: > > > > Just a small pedantry note - in a comparison section, the RFC > compares > > > > this syntax to function composition. But this is not function > > > > composition. This is a syntax sugar for calling two functions one > after > > > > another, not operator that produces a function. It sounds pedantic > but > > > > it's rather important distinction - if |> is composition, than $foo > |> > > > > $bar is a new callable provided $foo and $bar are callable (but no > > > > function is actually being called here!). If |> is call syntax, it's > > > > actually the result of calling $bar($foo). > > > > > > > > So comparing it to function composition is a bit confusing. > Otherwise it > > > > looks OK to me, except the syntax for calling functions and methods > is a > > > > bit awkward, but it's not the problem of this RFC I imagine. > > > > > > I'm not sure I follow. The only place composition is mentioned is in > the F# section, where it calls out specifically that we're implementing > "pipe forward" and *not* the composition operators ( >> ). Is that unclear? > > > > Actually it's also mentioned in the Haskell section, but as "function > > concatenation" (which adds to the confusion I guess). > > Ah, I see what you mean. I've adjusted that section to be clearer about > what Haskell does. > > > Speaking of Haskell, that reminded me of > > http://learnyouahaskell.com/a-fistful-of-monads#walk-the-line where > > the author defines a custom `-:` operator such that `x -: f` desugars > > to `f x` (equivalent to how `$x |> $f` would desugar to `$f($x)` with > > the RFC), which allows to write e.g. `xs -: sort -: reverse` instead > > of `reverse (sort xs)` or `(reverse . sort) xs`. > > If I ever actually wrote Haskell, I'd find that extremely useful. :-) > > --Larry Garfield > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
-- Best regards, Bruce Weirdan mailto: weirdan@gmail.com

Guilliam Xavier

6 years ago
On Wed, Apr 22, 2020 at 11:32 PM Bruce Weirdan <weirdan@gmail.com> wrote:
> > Haskell has & operator in Data.Function module which is exact equivalent of > the proposed feature. > Link: > https://hackage.haskell.org/package/base-4.8.0.0/docs/Data-Function.html#g:2 > Example: https://repl.it/repls/KindLightsalmonApplicationserver
Ah, thanks for the intel! (I see it was added in 2015, i.e. a few years after the publication of LYAHFGG in 2011)
-- Guilliam Xavier

Larry Garfield

6 years ago
On Tue, Apr 21, 2020, at 9:43 AM, Larry Garfield wrote:
> On Mon, Apr 20, 2020, at 11:06 PM, Sara Golemon wrote: > > Happy with the revival, and I do want to underline the "Future Work" > > section about Partial Functions. PF will make this version of pipes 100% > > more readable and fluent, and I want to endorse both, but I'll take one at > > a time. > > I agree that the pipe operator relies on having a "good" (subjective) > way to reference callables, which is a weakness of PHP right now. We > discussed that in depth a few months ago as well. > > IMO, short-lambdas in 7.4 are "good enough" to make the pipe operator > useful. They're not ideal, but they're sufficient. > > Levi's partial application RFC would be close enough to ideal; You > could reference an already-single-parameter or multi-parameter function > lexically without any string parsing silliness, which means namespace > resolution works, and it could potentially be optimized to avoid an > extra layer of function call. It would be even better than ::function, > IMO, because you can provide it with closed-over variables at the same > time. It sounds like we all want that. > > I do not believe that should block the pipe operator, however. This is > a case where two independent RFCs should be able to stand on their own > but create a "sum is greater than their parts" situation, which is a > good thing. > > If anyone has bandwidth, I'm happy to collaborate on the PF RFC as > well. It's out of my solo skillset right now (frankly so was pipe > until this weekend), but if someone is able to provide > hand-holding/pairing I'm willing to work on that, too, either now in > parallel or after pipe lands. Both should happen, but I don't think > either should block the other.
Just an update: I've tweaked the patch based on feedback from a few folks. The precedence now seems to be where it is most logical (to me), and I've changed what the flag is on the AST node based on a recommendation from Nikita (assuming I understood him correctly). I think it's in an acceptable final state at the moment baring any further feedback on GitHub. It sounds like the only negative pushback at the moment is "can we do partial application first?" Which... I'd love to but we need help on that. :-) (Volunteers welcome.) Is there any other pushback? How do people feel about putting this to a vote sometime next week? I don't know how long I want to sit on it waiting to see if we can pull off partial application. I also want the partial application RFC, for reasons stated, but still feel that both are beneficial enough to stand on their own and it doesn't really matter which happens first. --Larry Garfield

Nikita Popov

6 years ago
On Wed, Apr 29, 2020 at 2:35 AM Larry Garfield <larry@garfieldtech.com> wrote:
> On Tue, Apr 21, 2020, at 9:43 AM, Larry Garfield wrote: > > > On Mon, Apr 20, 2020, at 11:06 PM, Sara Golemon wrote: > > > Happy with the revival, and I do want to underline the "Future Work" > > > section about Partial Functions. PF will make this version of pipes > 100% > > > more readable and fluent, and I want to endorse both, but I'll take > one at > > > a time. > > > > I agree that the pipe operator relies on having a "good" (subjective) > > way to reference callables, which is a weakness of PHP right now. We > > discussed that in depth a few months ago as well. > > > > IMO, short-lambdas in 7.4 are "good enough" to make the pipe operator > > useful. They're not ideal, but they're sufficient. > > > > Levi's partial application RFC would be close enough to ideal; You > > could reference an already-single-parameter or multi-parameter function > > lexically without any string parsing silliness, which means namespace > > resolution works, and it could potentially be optimized to avoid an > > extra layer of function call. It would be even better than ::function, > > IMO, because you can provide it with closed-over variables at the same > > time. It sounds like we all want that. > > > > I do not believe that should block the pipe operator, however. This is > > a case where two independent RFCs should be able to stand on their own > > but create a "sum is greater than their parts" situation, which is a > > good thing. > > > > If anyone has bandwidth, I'm happy to collaborate on the PF RFC as > > well. It's out of my solo skillset right now (frankly so was pipe > > until this weekend), but if someone is able to provide > > hand-holding/pairing I'm willing to work on that, too, either now in > > parallel or after pipe lands. Both should happen, but I don't think > > either should block the other. > > Just an update: I've tweaked the patch based on feedback from a few > folks. The precedence now seems to be where it is most logical (to me), > and I've changed what the flag is on the AST node based on a recommendation > from Nikita (assuming I understood him correctly). I think it's in an > acceptable final state at the moment baring any further feedback on GitHub. > > It sounds like the only negative pushback at the moment is "can we do > partial application first?" Which... I'd love to but we need help on that. > :-) (Volunteers welcome.) Is there any other pushback? > > How do people feel about putting this to a vote sometime next week? I > don't know how long I want to sit on it waiting to see if we can pull off > partial application. I also want the partial application RFC, for reasons > stated, but still feel that both are beneficial enough to stand on their > own and it doesn't really matter which happens first. >
I think it's pretty unlikely that this RFC will pass without having partial application first. I understand that the features are technically orthogonal, but in terms of coding style, none of the examples given in the RFC would pass code review (from me). They either use PHP's ugly callable syntax, or they use arrow functions, in which case you could just as well store the value in a temporary $x, rather than using an fn($x) function combined with the pipe operator. As-is, I don't think the pipe operator would constitute an improvement over the status quo. So... feel free to start voting, but don't get expectations too high ;) Small note on the RFC text: The line |> fn($x) => array_map(fn($v) => 'strtoupper', $x) should probably be just |> fn($x) => array_map('strtoupper', $x) Regards, Nikita

Mike Schinkel

6 years ago
A questions and a comment: 1. How will XDEBUG handle a multiline construct such as this? Will it treat as one expression, or allow breakpoints at each "link" in the pipe chain? Maybe this is a better question for Derek Rethans? 2. The exclusive use of callable seems problematic prior to an inclusion a '::function' operator that can be applied to functions for symbol resolution to function name as it will encourage more use of strings that are not checked by PHP. Maybe we could address that first? Also #2 would be true for method names although how that might be implemented is a bit more complicated. -Mike

Stas Malyshev

6 years ago
Hi!
> https://wiki.php.net/rfc/pipe-operator-v2
Just a small pedantry note - in a comparison section, the RFC compares this syntax to function composition. But this is not function composition. This is a syntax sugar for calling two functions one after another, not operator that produces a function. It sounds pedantic but it's rather important distinction - if |> is composition, than $foo |> $bar is a new callable provided $foo and $bar are callable (but no function is actually being called here!). If |> is call syntax, it's actually the result of calling $bar($foo). So comparing it to function composition is a bit confusing. Otherwise it looks OK to me, except the syntax for calling functions and methods is a bit awkward, but it's not the problem of this RFC I imagine.
-- Stas Malyshev smalyshev@gmail.com

Nikita Popov

6 years ago
On Tue, Apr 21, 2020 at 3:39 AM Larry Garfield <larry@garfieldtech.com> wrote:
> Hello fine people of Internals! > > I've been commenting on other RFCs enough lately that I should probably > put myself through the wringer, too. I therefore offer this RFC to add a > function pipe operator, as seen in a number of other languages: > > https://wiki.php.net/rfc/pipe-operator-v2 > > A patch is available here: > > https://github.com/php/php-src/pull/5425 > > Although I have one or two bits of it that I could use feedback on. (Let > me know over on GitHub.) > > It's deliberately a very small, lightweight, and targeted change. The > Future Work section suggests possible additions for later, but this is as > much as my meager skills can manage for the moment. > > My enormous thanks to Sammy K Powers and Levi Morrison for their copious > hand-holding in my first foray into php-src. > > *dons flame retardant suit* >
I like the basic idea, but don't think it makes sense to introduce the syntax *before* we have first-class support for partial function application. Without it, you're forced to use PHP's awkward callable syntax, which imho makes for a regression over writing things the normal way. Nikita

Marco Pivetta

6 years ago
Hey Larry, This looks good! One question arises: how do exception traces look like, if they happen mid-pipe? Is the pipe effectively attaching stack frames to the entire thing, or is it flattening the trace? Greets, Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/ On Tue, Apr 21, 2020 at 3:38 AM Larry Garfield <larry@garfieldtech.com> wrote: