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
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
--
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.
Regards,
Nikita
<snip>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.
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.
What do you mean here by "partial application"?
cheers,
Derick
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):
incr = partial(plus, 1);
print incr(2); // 3
print incr(100); // 101
--
Christoph M. Becker
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.
What do you mean here by "partial application"?
$trimX = trim($$, $x);
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.
Essentially, the above turns into this:
$trimX = function($arg) use ($x) { return trim($arg, $x); };
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.
-Sara
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
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--
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:
$potentially_valid_emails =
array_filter($this->isProperlyFormattedEmailAddress($$), $email_list);
Anyway, the purpose of this thread is to talk about the pipe operator
so I'll stop talking now.
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).
https://github.com/php/php-src/compare/master...sgolemon:lambda
which provides a form of both short-closures and partial functions.
Combined with also-super-hacky pipe diff from earlier, you get:
$x = "Hello"
|> &{strtoupper($0)}
|> &{ $0 . "world" }
|> &{strrev($0)};
var_dump($x);
// string(10) "dlrowOLLEH"
-Sara
On Thu, Sep 21, 2017 at 5:13 PM, Stanislav Malyshev smalyshev@gmail.com
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).https://github.com/php/php-src/compare/master...sgolemon:lambda
which provides a form of both short-closures and partial functions.Combined with also-super-hacky pipe diff from earlier, you get:
$x = "Hello"
|> &{strtoupper($0)}
|> &{ $0 . "world" }
|> &{strrev($0)};var_dump($x);
// string(10) "dlrowOLLEH"-Sara
--
By the way the &
is not required. When experimenting with possible short
closure syntax I created an implementation for {}
to make sure it was
possible:
$x = "Hello"
|> {strtoupper($0)}
|> { $0 . "world" }
|> {strrev($0)};
If people would prefer that syntax for short-closures then that's fine by
me. If we made it this concise there wouldn't be a need for a $$
proposal
anyway.
My impression from the community is that the pipe operator is desirable but
not if it encourages string or array callables. In that vein which syntax
do you prefer?
// brace style
$x = "Hello"
|> {strtoupper($0)}
|> { $0 . "world" }
|> {strrev($0)};
// fn style
$x = "Hello"
|> fn($x) => strtoupper($x)}
|> fn($x) => $x . "world"
|> fn($x) => strrev($0);
// caret style
$x = "Hello"
|> ^($x) => strtoupper($x)}
|> ^($x) => $x . "world"
|> ^($x) => strrev($0);
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?
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:
// Simple definition
{ $0 . ' world' }
{ $greeting => $greeting . ' world' )
// Nested definitions
{ $x => { in_array($x, $0) } }
{ $x => { $y => in_array($x, $y) } }
// 2-parameter function
{ $x, $y => in_array($x, $y) }
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')
Just throwing some thoughts out there to see if any of them stick...
--
Rowan Collins
[IMSoP]
"Rowan Collins" wrote in message
news:f9001e2a-8f13-d4ba-f514-f18dc1e4ff89@gmail.com...
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.
--
Tony Marston
2017-09-28 21:07 GMT+02:00 Levi Morrison levim@php.net:
On Thu, Sep 21, 2017 at 5:13 PM, Stanislav Malyshev <smalyshev@gmail.com
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).https://github.com/php/php-src/compare/master...sgolemon:lambda
which provides a form of both short-closures and partial functions.Combined with also-super-hacky pipe diff from earlier, you get:
$x = "Hello"
|> &{strtoupper($0)}
|> &{ $0 . "world" }
|> &{strrev($0)};var_dump($x);
// string(10) "dlrowOLLEH"-Sara
--
By the way the
&
is not required. When experimenting with possible short
closure syntax I created an implementation for{}
to make sure it was
possible:$x = "Hello"
|> {strtoupper($0)}
|> { $0 . "world" }
|> {strrev($0)};If people would prefer that syntax for short-closures then that's fine by
me. If we made it this concise there wouldn't be a need for a$$
proposal
anyway.
I love this syntax with braces, I wanted to do like it in
https://wiki.php.net/rfc/short-closures
but only for replacement of string or array callables as closures.
My impression from the community is that the pipe operator is desirable but
not if it encourages string or array callables. In that vein which syntax
do you prefer?// brace style
$x = "Hello"
|> {strtoupper($0)}
|> { $0 . "world" }
|> {strrev($0)};// fn style
$x = "Hello"
|> fn($x) => strtoupper($x)}
|> fn($x) => $x . "world"
|> fn($x) => strrev($0);// caret style
$x = "Hello"
|> ^($x) => strtoupper($x)}
|> ^($x) => $x . "world"
|> ^($x) => strrev($0);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?
--
regards / pozdrawiam,
Michał Brzuchalski
about.me/brzuchal
brzuchalski.com
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:
$appendWorld = function ($x) {return "$x world";};
$x = compose('strtoupper', $appendWorld)('hello');
The benefit of using a compose function would be the possibility of
point-free programming without explicitely declaring another function:
$toUpperAppendWorld = compose(
'strtoupper',
function ($x) {return "$x world";}
);
$x = $toUpperAppendWorld('hello');
$y = $toUpperAppendWorld('goodbye');
Basically, the compose function would be like the pipe operator without
special casing the (first) input.
See also Andreas's suggestion regarding built-in functions for common
functional primitives (http://news.php.net/php.internals/100739).
--
Christoph M. Becker