[PHP-DEV] [RFC] Pre-draft for PipeOp v2

I was planning to update the RFC, but wiki.php.net is having issues
atm and isn't coming back up with basic coaxing, so I'll just start
discussion informally, and the RFC can be updates later.

Background: I made an RFC some time ago to implement HackLang's Pipe
Operator https://docs.hhvm.com/hack/operators/pipe-operator which
provides fluent calling for non-object interfaces.
I circulated it to mixed reviews, many negative, with the negativity
feeling like it centered on the use of a magic placeholder token `$$`.

Non-Goal: I didn't include support for base function names (e.g.
`"hello" |> strtoupper`) because of the conflict with constants.
Using a constant to store your function name is totes legit and
consistent with language syntax.

Future Scope: Short Lambdas `$x => $x + 1` and Partial Functions
`someFunc('fixed val1', ..., 'fixed val2')` would help make this
functionality more useful and are worth discussing as a sub-thread,
but are not required to be implemented at the same time.

On Tue, Sep 19, 2017 at 4:32 PM, Sara Golemon <[email protected]> wrote:
> I was planning to update the RFC, but wiki.php.net is having issues
> atm and isn't coming back up with basic coaxing, so I'll just start
> discussion informally, and the RFC can be updates later.
>
> Background: I made an RFC some time ago to implement HackLang's Pipe
> Operator https://docs.hhvm.com/hack/operators/pipe-operator which
> provides fluent calling for non-object interfaces.
> I circulated it to mixed reviews, many negative, with the negativity
> feeling like it centered on the use of a magic placeholder token `$$`.
>
> After discussion with Levi and others who suggested a simpler
> approach, I'd like to offer
> https://github.com/php/php-src/compare/master...sgolemon:pipe2 as an
> alternate possibility.
>
> This version removes the $$ token, and instead treats the RHS of the
> expression as a callable obeying the same rules as the callable
> typehint elsewhere in the language. Specifically:
> * Free functions as strings containing the function name. (e.g. 'funcname')
> * Object methods as array($object, 'methodname')
> * Static methods as array('Classname', 'methodname')
> * Closure expression (e.g. function($x) { return ...; } )
> * Object instance with an __invoke() method.
>
> In a given pipe expression, the output of the LHS expression feeds a
> single arg to the callable on the RHS.
> Examples:
>
> $x = "hello"
> |> 'strtoupper'
> |> function($x) { return $x . " world"; };
> // $x === "HELLO world"
>
> Non-Goal: I didn't include support for base function names (e.g.
> `"hello" |> strtoupper`) because of the conflict with constants.
> Using a constant to store your function name is totes legit and
> consistent with language syntax.
>
> Future Scope: Short Lambdas `$x => $x + 1` and Partial Functions
> `someFunc('fixed val1', ..., 'fixed val2')` would help make this
> functionality more useful and are worth discussing as a sub-thread,
> but are not required to be implemented at the same time.
>
> -Sara
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php

Thank you for making this proposal! As alluded to I had concerns on
the original proposal and fully support this version. Sometimes
simplicity is the mark of a good RFC and I think this is one of those
times.

> I was planning to update the RFC, but wiki.php.net is having issues
> atm and isn't coming back up with basic coaxing, so I'll just start
> discussion informally, and the RFC can be updates later.
>
> Background: I made an RFC some time ago to implement HackLang's Pipe
> Operator https://docs.hhvm.com/hack/operators/pipe-operator which
> provides fluent calling for non-object interfaces.
> I circulated it to mixed reviews, many negative, with the negativity
> feeling like it centered on the use of a magic placeholder token `$$`.
>
> After discussion with Levi and others who suggested a simpler
> approach, I'd like to offer
> https://github.com/php/php-src/compare/master...sgolemon:pipe2 as an
> alternate possibility.
>
> This version removes the $$ token, and instead treats the RHS of the
> expression as a callable obeying the same rules as the callable
> typehint elsewhere in the language. Specifically:
> * Free functions as strings containing the function name. (e.g. 'funcname')
> * Object methods as array($object, 'methodname')
> * Static methods as array('Classname', 'methodname')
> * Closure expression (e.g. function($x) { return ...; } )
> * Object instance with an __invoke() method.
>
> In a given pipe expression, the output of the LHS expression feeds a
> single arg to the callable on the RHS.
> Examples:
>
> $x = "hello"
> |> 'strtoupper'
> |> function($x) { return $x . " world"; };
> // $x === "HELLO world"
>
> Non-Goal: I didn't include support for base function names (e.g.
> `"hello" |> strtoupper`) because of the conflict with constants.
> Using a constant to store your function name is totes legit and
> consistent with language syntax.
>
> Future Scope: Short Lambdas `$x => $x + 1` and Partial Functions
> `someFunc('fixed val1', ..., 'fixed val2')` would help make this
> functionality more useful and are worth discussing as a sub-thread,
> but are not required to be implemented at the same time.
>

I think this feature makes very little sense if it's not introduced
together with a way of making partial application much more ergonomic than
it is now. I understand the desire to keep things separate, but I don't
think that this proposal can land without partial application syntax being
introduced beforehand or in conjunction with it.

From a previous R11 discussion, I remember that two strong contenders were
foo(...) for getting a callable with an arbitrary number of unbound
parameters and foo($$) for a single unbound parameter. In the latter case
the proposal has a similar expressive power as the previous pipe operator
proposal. Another option was to make $$ a more general construct for
obtaining single-parameter closures in compact form. Then you would also be
able to write "$$->getFoo()" to get the equivalent of "function($x) {
return $x->getFoo(); }" and similar. A disadvantage is that the precedence
here is less clear and that a short closure syntax might be more clear for
the cases that go beyond partial application.

I was planning to update the RFC, but wiki.php.net is having issues
atm and isn't coming back up with basic coaxing, so I'll just start
discussion informally, and the RFC can be updates later.

Background: I made an RFC some time ago to implement HackLang's Pipe
Operator https://docs.hhvm.com/hack/operators/pipe-operator which
provides fluent calling for non-object interfaces.
I circulated it to mixed reviews, many negative, with the negativity
feeling like it centered on the use of a magic placeholder token `$$`.

Non-Goal: I didn't include support for base function names (e.g.
`"hello" |> strtoupper`) because of the conflict with constants.
Using a constant to store your function name is totes legit and
consistent with language syntax.

Future Scope: Short Lambdas `$x => $x + 1` and Partial Functions
`someFunc('fixed val1', ..., 'fixed val2')` would help make this
functionality more useful and are worth discussing as a sub-thread,
but are not required to be implemented at the same time.

I think this feature makes very little sense if it's not introduced
together with a way of making partial application much more ergonomic than
it is now. I understand the desire to keep things separate, but I don't
think that this proposal can land without partial application syntax being
introduced beforehand or in conjunction with it.

From a previous R11 discussion, I remember that two strong contenders were
foo(...) for getting a callable with an arbitrary number of unbound
parameters and foo($$) for a single unbound parameter. In the latter case
the proposal has a similar expressive power as the previous pipe operator
proposal. Another option was to make $$ a more general construct for
obtaining single-parameter closures in compact form. Then you would also be
able to write "$$->getFoo()" to get the equivalent of "function($x) {
return $x->getFoo(); }" and similar. A disadvantage is that the precedence
here is less clear and that a short closure syntax might be more clear for
the cases that go beyond partial application.

> On Wed, Sep 20, 2017 at 6:32 AM, Sara Golemon <[email protected]> wrote:
>
> > I was planning to update the RFC, but wiki.php.net is having issues
> > atm and isn't coming back up with basic coaxing, so I'll just start
> > discussion informally, and the RFC can be updates later.

<snip>

> > Non-Goal: I didn't include support for base function names (e.g.
> > `"hello" |> strtoupper`) because of the conflict with constants.
> > Using a constant to store your function name is totes legit and
> > consistent with language syntax.
> >
> > Future Scope: Short Lambdas `$x => $x + 1` and Partial Functions
> > `someFunc('fixed val1', ..., 'fixed val2')` would help make this
> > functionality more useful and are worth discussing as a sub-thread,
> > but are not required to be implemented at the same time.
> >
>
> I think this feature makes very little sense if it's not introduced
> together with a way of making partial application much more ergonomic than
> it is now.

> On Wed, 20 Sep 2017, Nikita Popov wrote:
>
>> I think this feature makes very little sense if it's not introduced
>> together with a way of making partial application much more ergonomic than
>> it is now.
>
> What do you mean here by "partial application"?

Partial application is a common concept in functional/applicative
programming languages, which facilitates to get a closure over another
function where some of the parameters are already applied, e.g.
something like (pseudocode):

On Wed, Sep 20, 2017 at 12:52 PM, Derick Rethans <[email protected]> wrote:
> On Wed, 20 Sep 2017, Nikita Popov wrote:
>> > Future Scope: Short Lambdas `$x => $x + 1` and Partial Functions
>> > `someFunc('fixed val1', ..., 'fixed val2')` would help make this
>> > functionality more useful and are worth discussing as a sub-thread,
>> > but are not required to be implemented at the same time.
>> >
>>
>> I think this feature makes very little sense if it's not introduced
>> together with a way of making partial application much more ergonomic than
>> it is now.
>>
I generally agree with this statement, and it's why the original
pipe-op (as with hacklang's version) included (a form of) pfa
implicitly. If that means we should nail down the specifics of PFA
and/or Short-Lambdas before even bothering to move into full draft of
PipeOp, then that's fine.

Levi made the very solid argument that they can be independently
considered even if they compliment each other well, and given the
triviality of the pipe half of that equation, I put it together first
so that we have something to look at.

On the topic of pipe2 specifically:
The positive side of this diff is that it has zero impact on the
backend compiler or opcache since it doesn't produce any new AST
elements. On the minus side, the AST it produces opaques the original
format. In practice, this hack is only visible if you're using your
or my ast extensions, or if you use a failing assert. This is what I
like the least about the approach I included in my link. It's not the
first place we've hidden original intent behind AST transformations,
but it's certainly the most visually jarring when serialized.

In this example, trim isn't invoked immediately, instead, $trimX
becomes a callable (closure) which takes a single argument and invokes
trim() with that argument and the captured $x variable. We've
"partially applied" some arguments to the trim() function, but we
haven't finished forming a function call yet.

The '$$' token used by HackLang's PipeOp works well enough because we
know that RHS is only ever going to receive one argument (the result
of LHS). A more generic approach to PFA would probably involve
allowing for multiple positional arguments, perhaps like:

$myInArray = in_array($2, $1); // Reverse the arg order, effectively

PFA and ShortLambdas have the same scope capture issues however, which
make them thornier issues to tackle.

I am still feeling it is a bit weird to do things that way, but other
than that (which is just my personal issue) I do not see any problems in
this proposal. With magic weird $$ thing gone, it seems to be
straightforward and mostly intuitive. So while I am not feeling this is
a must, I think it may be nice to have it.

It'd be also nice then if we could have some syntax that allowed us to
refer to functions/methods as callables - mostly for the benefit of the
code readers and IDEs. I.e. you can do "hello" |> "strtoupper" and it's
fine but it is not at all intuitive what you're doing sending one string
into another. Same with "hello" |> [$this, 'bar']. Now, if we could do
something like this:
"hello" |> &{strtoupper}
"hello" |> &{$this->bar}

(yes I know the syntax is ugly, I hope you get the idea though to have a
syntax for this) then you could have better readability, easier IDE
autocompletion and other benefits. Not for this RFC, clearly, but just
putting it out there so I don't forget.

On Thu, Sep 21, 2017 at 3:13 PM, Stanislav Malyshev <[email protected]> wrote:
> Hi!
>
>> After discussion with Levi and others who suggested a simpler
>> approach, I'd like to offer
>> https://github.com/php/php-src/compare/master...sgolemon:pipe2 as an
>> alternate possibility.
>
> I am still feeling it is a bit weird to do things that way, but other
> than that (which is just my personal issue) I do not see any problems in
> this proposal. With magic weird $$ thing gone, it seems to be
> straightforward and mostly intuitive. So while I am not feeling this is
> a must, I think it may be nice to have it.
>
> It'd be also nice then if we could have some syntax that allowed us to
> refer to functions/methods as callables - mostly for the benefit of the
> code readers and IDEs. I.e. you can do "hello" |> "strtoupper" and it's
> fine but it is not at all intuitive what you're doing sending one string
> into another. Same with "hello" |> [$this, 'bar']. Now, if we could do
> something like this:
> "hello" |> &{strtoupper}
> "hello" |> &{$this->bar}
>
> (yes I know the syntax is ugly, I hope you get the idea though to have a
> syntax for this) then you could have better readability, easier IDE
> autocompletion and other benefits. Not for this RFC, clearly, but just
> putting it out there so I don't forget.
>
> --
> Stas Malyshev
> smalyshev@gmail.com
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php

I don't want to get too far off topic but since Stas has wandered off
a bit already I'll mention this. Scala has "placeholders":

val sum = List(1,2,3,4,5).reduceLeft(_+_)

The expression `_ + _` will create a function that takes two
parameters and then returns the result of adding them together,
equivalent to this:

val sum = List(1,2,3,4,5).reduceLeft((a, b) => a + b)

The single underscore `_` might not work for PHP because `gettext` has
used a single underscore as an alias for... a long time, maybe since
its inception. But for this example I'll use it anyway:

"hello" |> strtoupper(_) |> $this->bar(_)

It's saying, pretty literally, fill in the blank. Remember, though,
it's actually creating closures and doesn't directly mean "put the
result of the left-hand side here" unlike the previous proposal for
`$$`.

I brought it up because this can fill the same need as Stas' proposal
creating callables with something like `&{strtoupper}`. If we used
the symbol `$$` for the same placeholder mechanism from Scala it
becomes a more general purpose mechanism that could be used for any
callables, not just ones involved in a pipe:

On Thu, Sep 21, 2017 at 5:13 PM, Stanislav Malyshev <[email protected]> wrote:
> It'd be also nice then if we could have some syntax that allowed us to
> refer to functions/methods as callables - mostly for the benefit of the
> code readers and IDEs. I.e. you can do "hello" |> "strtoupper" and it's
> fine but it is not at all intuitive what you're doing sending one string
> into another. Same with "hello" |> [$this, 'bar']. Now, if we could do
> something like this:
> "hello" |> &{strtoupper}
> "hello" |> &{$this->bar}
>
Super-hacky implementation (that I wouldn't want to merge, but it
shows the syntax at work).

I included these two styles because they are the most promising versions
from the arrow functions discussions. I think my preferred order is the one
I wrote them in. The brace style is concise, nicely delimits the
expression, and seems the clearest for me to read because the symbols don't
dominate. The fn style seems nicer than caret which has too many symbols in
close proximity for my taste.

Community thoughts? Which short closure style pairs the nicest with the
pipe operator?

On 28/09/2017 20:07, Levi Morrison wrote:
> The brace style is concise, nicely delimits the
> expression, and seems the clearest for me to read because the symbols don't
> dominate.

This is something that I tried to push for in previous discussions -
I've never liked the syntaxes where the expression floats away from the
operator, and you have to work out where it ends. The counter-argument I
got was that some people actually like writing things like this,
although I'm not entirely clear why:

fn($x) => fn($y) => in_array($x, $y)

Which brings us to the question of how we might generalise the {} syntax
for more complex situations. Clearly, we can't just nest them if the
first parameter is always called $0:

{ { in_array($0, $0) } } #WAT?

Obviously named variables are kind of useful anyway, so maybe $0 could
be the default, but allow them to be specified:

> My impression from the community is that the pipe operator is
desirable but
> not if it encourages string or array callables.

Yeah, my first thought on this thread that I liked this version of the
proposal significantly less than the previous one, because I always
liked the idea of this being a programming style, with the RHS being a
statement to be rewritten. So you could write $input |> $foo = $$ |>
echo $$ |> return $$ ...

But with a suitable way of creating a callable to pass to it, I could
live with it.

What if the syntax for taking a reference to function was just a special
case of the partial application / lambda syntax? At risk of turning into
Perl, we could use "$..." to mean "copy in all the parameters":

$foo = { in_array($...) };

This would be logically equivalent to:

$foo = function(...$x) { return in_array(...$x); }

But the compiler could actually optimise it as something more like
Closure::fromCallable('in_array')

"Rowan Collins" wrote in message
news:[email protected]
>
>On 28/09/2017 20:07, Levi Morrison wrote:
>> The brace style is concise, nicely delimits the
>> expression, and seems the clearest for me to read because the symbols
>> don't
>> dominate.
>
>
>This is something that I tried to push for in previous discussions - I've
>never liked the syntaxes where the expression floats away from the
>operator, and you have to work out where it ends. The counter-argument I
>got was that some people actually like writing things like this, although
>I'm not entirely clear why:
>
>fn($x) => fn($y) => in_array($x, $y)
>

Just because some people would like to write code like this does not make it
acceptable for the majority of the programming community. You should never
forget that the primary aim of a programmer is to write code which can be
read by a human, and only incidentally to be executed by a machine (H.
Abelson and G. Sussman in "The Structure and Interpretation of Computer
Programs", 1984).

Some people complain that PHP is too verbose, so they strive to replace long
words, or groups of words, with abbreviations or even symbols. This, IMHO,
converts a readable program into a bunch of hieroglyphics and should
therefore be avoided.

I think there should be a rule which states that if something can already be
done with 5 lines or less of userland code then it should not be built into
the core language as it would be adding unnecessary complications that would
only benefit a small minority of programmers but would be to the detriment
of the majority.

> I was planning to update the RFC, but wiki.php.net is having issues
> atm and isn't coming back up with basic coaxing, so I'll just start
> discussion informally, and the RFC can be updates later.
>
> Background: I made an RFC some time ago to implement HackLang's Pipe
> Operator https://docs.hhvm.com/hack/operators/pipe-operator which
> provides fluent calling for non-object interfaces.
> I circulated it to mixed reviews, many negative, with the negativity
> feeling like it centered on the use of a magic placeholder token `$$`.
>
> After discussion with Levi and others who suggested a simpler
> approach, I'd like to offer
> https://github.com/php/php-src/compare/master...sgolemon:pipe2 as an
> alternate possibility.
>
> This version removes the $$ token, and instead treats the RHS of the
> expression as a callable obeying the same rules as the callable
> typehint elsewhere in the language. Specifically:
> * Free functions as strings containing the function name. (e.g. 'funcname')
> * Object methods as array($object, 'methodname')
> * Static methods as array('Classname', 'methodname')
> * Closure expression (e.g. function($x) { return ...; } )
> * Object instance with an __invoke() method.
>
> In a given pipe expression, the output of the LHS expression feeds a
> single arg to the callable on the RHS.
> Examples:
>
> $x = "hello"
> |> 'strtoupper'
> |> function($x) { return $x . " world"; };
> // $x === "HELLO world"
>
> Non-Goal: I didn't include support for base function names (e.g.
> `"hello" |> strtoupper`) because of the conflict with constants.
> Using a constant to store your function name is totes legit and
> consistent with language syntax.

I'm not particularly fond of a pipe(line) operator. I don't see a great
advantage over a simple compose function: