I had this RFC in draft since some time, but delayed it due to all the ongoing PHP 7 discussions. Also we have no master branch to merge features in until 5.4 EOL. Thus I'm reviving this now.
Time for the first RFC targeting PHP 7.1 (assuming PHP 8 isn't going to be the next version ;-)):
The short Closures RFC:
https://wiki.php.net/rfc/short_closures
Hoping for constructive feedback,
Bob
Hi!
I had this RFC in draft since some time, but delayed it due to all
the ongoing PHP 7 discussions. Also we have no master branch to merge
features in until 5.4 EOL. Thus I'm reviving this now.Time for the first RFC targeting PHP 7.1 (assuming PHP 8 isn't going
to be the next version ;-)):The short Closures RFC: https://wiki.php.net/rfc/short_closures
I personally don't see much advantage in making PHP code less readable
and harder to parse. Also, "automatically use () all of the (compiled)
variables" does not look like a good idea - first, users have no idea
what compiled variables are, so the result would be unpredictable for
them, second, if there are a lot of variables in the scope (like global
scope) it would have huge performance costs for no gain.
Also, this syntax does not allow "use by-ref" which the existing syntax
does allow.
I think we already have syntax for anonymous functions and it works. The
only addition in this RFC is automatically gobbling up all local
variables, which in vast majority of use cases is not what the function
needs.
Stas Malyshev
smalyshev@gmail.com
On Tue, 01 Sep 2015 00:05:35 +0300, Stanislav Malyshev
smalyshev@gmail.com wrote:
Hi!
I had this RFC in draft since some time, but delayed it due to all
the ongoing PHP 7 discussions. Also we have no master branch to merge
features in until 5.4 EOL. Thus I'm reviving this now.Time for the first RFC targeting PHP 7.1 (assuming PHP 8 isn't going
to be the next version ;-)):The short Closures RFC: https://wiki.php.net/rfc/short_closures
I personally don't see much advantage in making PHP code less readable
and harder to parse. Also, "automatically use () all of the (compiled)
variables" does not look like a good idea - first, users have no idea
what compiled variables are, so the result would be unpredictable for
them, second, if there are a lot of variables in the scope (like global
scope) it would have huge performance costs for no gain.
Also, this syntax does not allow "use by-ref" which the existing syntax
does allow.I think we already have syntax for anonymous functions and it works. The
only addition in this RFC is automatically gobbling up all local
variables, which in vast majority of use cases is not what the function
needs.
Hi Stas,
Capture of all variables from the current scope is not the only addition
in this RFC. The main addition (which is extended by that btw) is making
the syntax shorter. Which, in turn, makes it easier to write and read
closures. I've had a huge lot of cases where I would benefit from it,
things like this:
$collection = array_filter(array_map(function (Type $el) { return
$el->getStuff(); }, $collection));
It would be much easier to read and write with the short syntax:
$collection = array_filter(array_map((Type $el) ~> $el->getStuff(),
$collection));
Just because, I'll say it, the less symbols you have to read the faster
you'll read it. To some extent, of course, a counter-example being Perl
language. But I don't think that we are even close to it.
Your point about capturing too much if you have too much in the global
scope is legit but in this case I'd say two things: you can still use long
closure syntax, and second you have a problem with global scope not short
closures.
You see the main use case for anon. functions is when you need a
lightweight callback for something. If it were a fat function I'd
probably make it named and write tests for it. But when it's a small
function, often consisting from only one expression it kinda bothers me
when the body of this function takes less keystrokes than 'function' and
'return' keywords...
Hi!
in this RFC. The main addition (which is extended by that btw) is making
the syntax shorter. Which, in turn, makes it easier to write and read
Shrtr dsnt lwys mks thngs esr 2 rd.
Sometimes it's quite the opposite.
It would be much easier to read and write with the short syntax:
$collection = array_filter(array_map((Type $el) ~> $el->getStuff(),
$collection));
I disagree. It is very hard in this syntax so see where function call
starts and where it ends, especially if there are more that one
parameter and/or more than one closure.
Just because, I'll say it, the less symbols you have to read the faster
you'll read it. To some extent, of course, a counter-example being Perl
language. But I don't think that we are even close to it.
Yes, in this instance we are. And that's not a good thing.
Your point about capturing too much if you have too much in the global
scope is legit but in this case I'd say two things: you can still use
long closure syntax, and second you have a problem with global scope not
short closures.
No, I don't have a problem with global scope - this proposal has a
problem with any scope that contains many variables. Actually, virtually
every scope contains more variables than closures need. Without this
capture there is no problem.
You see the main use case for anon. functions is when you need a
lightweight callback for something. If it were a fat function I'd
And capturing whole scope makes it anything but lightweight, especially
if you have big scope.
probably make it named and write tests for it. But when it's a small
function, often consisting from only one expression it kinda bothers me
when the body of this function takes less keystrokes than 'function' and
'return' keywords...
If keystrokes are a problem, there are lots of solutions - like IDEs
that allow to define macros. Saving keystrokes at the cost of
readability never was a principle in PHP, and I don't think we should
start now.
Stas Malyshev
smalyshev@gmail.com
Hey, thanks for your feedback, but I think you'd got one or two things wrong…
Am 01.09.2015 um 02:24 schrieb Stanislav Malyshev smalyshev@gmail.com:
Hi!
in this RFC. The main addition (which is extended by that btw) is making
the syntax shorter. Which, in turn, makes it easier to write and readShrtr dsnt lwys mks thngs esr 2 rd.
Sometimes it's quite the opposite.It would be much easier to read and write with the short syntax:
$collection = array_filter(array_map((Type $el) ~> $el->getStuff(),
$collection));I disagree. It is very hard in this syntax so see where function call
starts and where it ends, especially if there are more that one
parameter and/or more than one closure.
When I format my code weirdly, I'll have that issue too. Generally, if you have problems to read the code, if you'd write it with ~>, just use the classical function () use () {} syntax.
I'm not aiming at defacto abolishing the classical syntax (unlike [] which seems to have replaced array() everywhere in 5.4+ code...); in some contexts it's well readable (compared to ~>), especially, as you say, with three or more parameters.
The strong point of this RFC is quick callbacks (like a call to array_map), which just have one or two parameters. (Like shown in https://wiki.php.net/rfc/short_closures#extracting_data_from_an_array_and_summing_it)
Another big benefit is especially in more functional code, with partial applications. (Where you chain multiple closures)
I might be wrong, but I feel like the "clumsiness" of the classical syntax is especially harmful for that code and makes it harder to read (once you get a bit used to ~>, it will be easy to read, really).
Just because, I'll say it, the less symbols you have to read the faster
you'll read it. To some extent, of course, a counter-example being Perl
language. But I don't think that we are even close to it.Yes, in this instance we are. And that's not a good thing.
As said, there is a fine line between abusing the syntax and using it in horribly inapt scenarios, or using it where it really helps.
Your point about capturing too much if you have too much in the global
scope is legit but in this case I'd say two things: you can still use
long closure syntax, and second you have a problem with global scope not
short closures.No, I don't have a problem with global scope - this proposal has a
problem with any scope that contains many variables. Actually, virtually
every scope contains more variables than closures need. Without this
capture there is no problem.You see the main use case for anon. functions is when you need a
lightweight callback for something. If it were a fat function I'dAnd capturing whole scope makes it anything but lightweight, especially
if you have big scope.
From the RFC:
all variables used in the body of the anonymous function will automatically be bound to the anonymous function closure from the defining scope
The implementation is not capturing all the variables from the outer scope, but strictly only these accessed by the variables in the Closure itself. Hence that shouldn't be much of an issue.
probably make it named and write tests for it. But when it's a small
function, often consisting from only one expression it kinda bothers me
when the body of this function takes less keystrokes than 'function' and
'return' keywords...If keystrokes are a problem, there are lots of solutions - like IDEs
that allow to define macros. Saving keystrokes at the cost of
readability never was a principle in PHP, and I don't think we should
start now.
Agree, don't start with keystrokes here. They're maybe a nice side-effect, but that's really not a thing to argue about.
It's really not the goal of the RFC to decrease readability. That'd be insane.
--
Stas Malyshev
smalyshev@gmail.com
Thanks,
Bob
From the RFC:
all variables used in the body of the anonymous function will automatically be bound to the anonymous function closure from the defining scope
The implementation is not capturing all the variables from the outer scope, but strictly only these accessed by the variables in the Closure itself. Hence that shouldn't be much of an issue.
This magic scares me. I made a very deliberate decision In the very first implementation of PHP to avoid scope side-effects like this. Inside a function everything is local unless explicitly declared not to be. This has carried through for the past 20+ years in slightly different ways, but the basic rule has stayed consistent even for closures. Unless explicitly declared via a use clause, every variable inside a closure is local and no cross-scope variable name side-effects are possible. I am not convinced that this feature is worth breaking that longstanding basic tenet of the language.
-Rasmus
Am 01.09.2015 um 04:55 schrieb Rasmus Lerdorf rasmus@lerdorf.com:
From the RFC:
all variables used in the body of the anonymous function will automatically be bound to the anonymous function closure from the defining scope
The implementation is not capturing all the variables from the outer scope, but strictly only these accessed by the variables in the Closure itself. Hence that shouldn't be much of an issue.
This magic scares me. I made a very deliberate decision In the very first implementation of PHP to avoid scope side-effects like this. Inside a function everything is local unless explicitly declared not to be. This has carried through for the past 20+ years in slightly different ways, but the basic rule has stayed consistent even for closures. Unless explicitly declared via a use clause, every variable inside a closure is local and no cross-scope variable name side-effects are possible. I am not convinced that this feature is worth breaking that longstanding basic tenet of the language.
-Rasmus
I'd like to note that it's binding by value and not by reference nor true closing over.
There are no side effects, only importing of the variables.
Also, as stated, an important application is quick closures to be passed to e.g. array_map:
/* increment by $y */
$y = 10;
$array = array_map($x ~> $x + $y, $array);
Here it is very obvious that we want to import a variable. Especially, I wonder how
$array = array_map(function ($x) use ($y) { return $x + $y; }, $array);
is making such simple applications more readable? And especially, what value does it add here?
If you want to be scoping-safe, (e.g. in bigger Closures inside a large function), there always is the explicit syntax allowing you to do that.
Short Closures are a tool for certain use cases, not a swiss knife.
Bob
P.s.: Just a side-question ... Why did you then think, it's be a great idea for include/require to share scope?
Hi!
Here it is very obvious that we want to import a variable. Especially, I wonder how
$array = array_map(function ($x) use ($y) { return $x + $y; }, $array);
is making such simple applications more readable? And especially, what value does it add here?
It is making it more readable by explicitly specifying that we're using
function, that this function imports $y from parent scope and returns $x
- $y. Of course, a seasoned programmer that has years of experience in
functional languages would recognize this pattern, but that's not
exactly the main target audience of PHP.
And I do not think the argument "you don't have to use it" is a good one
for adding language syntax constructs. If it's in the language, you'd
have to deal with it, and you'd have to teach other people to deal with
it. It's not some function sitting in the extension that may not even be
loaded - it's part of language syntax. So if it would encourage
write-only code, you'd have to deal with it even if you don't personally
create write-only code.
Stas Malyshev
smalyshev@gmail.com
Stas,
On Mon, Aug 31, 2015 at 11:46 PM, Stanislav Malyshev
smalyshev@gmail.com wrote:
Hi!
Here it is very obvious that we want to import a variable. Especially, I wonder how
$array = array_map(function ($x) use ($y) { return $x + $y; }, $array);
is making such simple applications more readable? And especially, what value does it add here?It is making it more readable by explicitly specifying that we're using
function, that this function imports $y from parent scope and returns $x
- $y. Of course, a seasoned programmer that has years of experience in
functional languages would recognize this pattern, but that's not
exactly the main target audience of PHP.
I think that's quite a bit hyperbolic of an argument.
Most programming languages today have a "short form" closure or lambda syntax
HackLang: ($x) ==> $x + 1;
C++: [](int x) -> int { return x + 1; }
Java: (int x) -> x + 1;
Python: lambda x: x+1
Ruby: lambda |x| { x + 1 }
Rust: |x| x + 1
JavaScript (ES6): x => x + 1
C#: x => x + 1
Objective C: ^(int x) { return x + 1; }
Note that not a single one of those is a "functional" language. That's
every language in the top 10 index except PERL, PHP and VB.NET (though
VB has a similar syntax: function (x as Integer) x + 1),
I agree that at first it will feel a little bit weird, especially
given that PHP in general lacks syntactic sugar (we still don't have a
short-hand to initialize stdclass objects). We introduced [] back in
5.4, and largely it has made readability FAR better (despite some
people saying it wouldn't at the time).
And I do not think the argument "you don't have to use it" is a good one
for adding language syntax constructs. If it's in the language, you'd
have to deal with it, and you'd have to teach other people to deal with
it. It's not some function sitting in the extension that may not even be
loaded - it's part of language syntax. So if it would encourage
write-only code, you'd have to deal with it even if you don't personally
create write-only code.
I agree 100%. "you don't have to use it" sucks as an argument. However
I don't think that's what Bob meant. He was trying to convey that the
tool isn't always appropriate in all situations. You can craft code
that makes this new syntax look unreadable. But you can craft code
that makes this look far more readable.
Example:
function partial(callable $cb) {
return function($left) use ($cb) {
return function($right) use ($cb, $left) {
return $cb($left, $right);
};
};
}
vs:
function partial(callable $cb) {
return $left ~> $right ~> $cb($left, $right);
}
It may look weird at first, but consider that's because you're not
used to the syntax. To me, the top is far harder to follow because of
the shear number of tokens to follow, not to mention that I need to
pay attention to the use-blocks which may or may not be importing
certain variables. The bottom reads exactly how I would expect it to.
Both are completely explicit in what they do and how they behave.
That's my opinion anyway...
Anthony
Hi!
I agree that at first it will feel a little bit weird, especially
given that PHP in general lacks syntactic sugar (we still don't have a
short-hand to initialize stdclass objects). We introduced [] back in
5.4, and largely it has made readability FAR better (despite some
people saying it wouldn't at the time).
That doesn't mean now we have to drop all keywords and switch to
write-only style. Even [] was somewhat controversial, though it is the
most elementary syntax construct. Function call is in no way elementary
- it has non-trivial structure, and obscuring this structure by removing
keywords does not help readability.
tool isn't always appropriate in all situations. You can craft code
that makes this new syntax look unreadable. But you can craft code
The problem is not that I can. The problem is that anybody can, and a
lot of people would, unwittingly, once the expressions get more complex
that $x+1.
that makes this look far more readable.
Example:
function partial(callable $cb) {
return function($left) use ($cb) {
return function($right) use ($cb, $left) {
return $cb($left, $right);
};
};
}
It is a bit verbose but pretty clear what's going on here - function
returns a function that returns a function, and you can easily track
which variable goes where and how it gets to the end.
vs:
function partial(callable $cb) {
return $left ~> $right ~> $cb($left, $right);
}
It looks very pretty as the ASCII art, and no entry-level programmer
would have any idea at all what these arrows actually do and how this
thing is supposed to work. At least not without studying the manual very
closely for extended time. That's exactly the problem. People start
writing overly clever code that looks so pretty - and then other people
are left scratching their heads about what actually is happening there.
That's what I mean by write-only code.
It may look weird at first, but consider that's because you're not
used to the syntax. To me, the top is far harder to follow because of
Of course, with enough training you can get fluent even in APL. I know
people that are. But PHP is supposed to be on the other end of the
spectrum.
the shear number of tokens to follow, not to mention that I need to
pay attention to the use-blocks which may or may not be importing
certain variables. The bottom reads exactly how I would expect it to.
That's because you wrote it and know in advance what it is supposed to
do. It's not a good test of readability.
Stas Malyshev
smalyshev@gmail.com
Hey:
Hi!
I agree that at first it will feel a little bit weird, especially
given that PHP in general lacks syntactic sugar (we still don't have a
short-hand to initialize stdclass objects). We introduced [] back in
5.4, and largely it has made readability FAR better (despite some
people saying it wouldn't at the time).That doesn't mean now we have to drop all keywords and switch to
write-only style. Even [] was somewhat controversial, though it is the
most elementary syntax construct. Function call is in no way elementary
- it has non-trivial structure, and obscuring this structure by removing
keywords does not help readability.tool isn't always appropriate in all situations. You can craft code
that makes this new syntax look unreadable. But you can craft codeThe problem is not that I can. The problem is that anybody can, and a
lot of people would, unwittingly, once the expressions get more complex
that $x+1.that makes this look far more readable.
Example:
function partial(callable $cb) {
return function($left) use ($cb) {
return function($right) use ($cb, $left) {
return $cb($left, $right);
};
};
}It is a bit verbose but pretty clear what's going on here - function
returns a function that returns a function, and you can easily track
which variable goes where and how it gets to the end.vs:
function partial(callable $cb) {
return $left ~> $right ~> $cb($left, $right);
}It looks very pretty as the ASCII art, and no entry-level programmer
would have any idea at all what these arrows actually do and how this
thing is supposed to work. At least not without studying the manual very
closely for extended time. That's exactly the problem. People start
writing overly clever code that looks so pretty - and then other people
are left scratching their heads about what actually is happening there.
That's what I mean by write-only code.It may look weird at first, but consider that's because you're not
used to the syntax. To me, the top is far harder to follow because ofOf course, with enough training you can get fluent even in APL. I know
people that are. But PHP is supposed to be on the other end of the
spectrum.the shear number of tokens to follow, not to mention that I need to
pay attention to the use-blocks which may or may not be importing
certain variables. The bottom reads exactly how I would expect it to.That's because you wrote it and know in advance what it is supposed to
do. It's not a good test of readability.
+1, that is what readable(guessable) means.....
thanks
--
Stas Malyshev
smalyshev@gmail.com--
--
Xinchen Hui
@Laruence
http://www.laruence.com/
function partial(callable $cb) {
return $left ~> $right ~> $cb($left, $right);
}
It looks very pretty as the ASCII art, and no entry-level programmer
would have any idea at all what these arrows actually do and how this
thing is supposed to work.
Only entry level programmers?
--
Lester Caine - G8HFL
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk
Hi Anthony and Bob,
Most programming languages today have a "short form" closure or lambda syntax
HackLang: ($x) ==> $x + 1;
C++: [](int x) -> int { return x + 1; }
Java: (int x) -> x + 1;
Python: lambda x: x+1
Ruby: lambda |x| { x + 1 }
Rust: |x| x + 1
JavaScript (ES6): x => x + 1
C#: x => x + 1
Objective C: ^(int x) { return x + 1; }
Nice summary!
The syntax may look strange at first, but proposed syntax is close enough to
other languages. There will be no barrier for our users in the long run.
I thought scope variables enabled by default is destructive at first,
but Bob clarify
they are passed by value. Therefore, it would not be issue.
Bob, is there reason not to use the same syntax as Hack "==>"?
Regards,
--
Yasuo Ohgaki
yohgaki@ohgaki.net
Hi Anthony and Bob,
On Tue, Sep 1, 2015 at 2:53 PM, Anthony Ferrara ircmaxell@gmail.com
wrote:Most programming languages today have a "short form" closure or lambda
syntaxHackLang: ($x) ==> $x + 1;
C++: [](int x) -> int { return x + 1; }
Java: (int x) -> x + 1;
Python: lambda x: x+1
Ruby: lambda |x| { x + 1 }
Rust: |x| x + 1
JavaScript (ES6): x => x + 1
C#: x => x + 1
Objective C: ^(int x) { return x + 1; }Nice summary!
The syntax may look strange at first, but proposed syntax is close enough
to
other languages. There will be no barrier for our users in the long run.I thought scope variables enabled by default is destructive at first,
but Bob clarify
they are passed by value. Therefore, it would not be issue.Bob, is there reason not to use the same syntax as Hack "==>"?
https://wiki.php.net/rfc/short_closures#symbol_choice
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
"Yasuo Ohgaki" wrote in message
news:CAGa2bXZpUd0J86D-VLSC+fUKhzRJk_3Qu_5afkG0bM+DE7XYew@mail.gmail.com...
Hi Anthony and Bob,
On Tue, Sep 1, 2015 at 2:53 PM, Anthony Ferrara ircmaxell@gmail.com
wrote:Most programming languages today have a "short form" closure or lambda
syntaxHackLang: ($x) ==> $x + 1;
C++: [](int x) -> int { return x + 1; }
Java: (int x) -> x + 1;
Python: lambda x: x+1
Ruby: lambda |x| { x + 1 }
Rust: |x| x + 1
JavaScript (ES6): x => x + 1
C#: x => x + 1
Objective C: ^(int x) { return x + 1; }Nice summary!
The syntax may look strange at first, but proposed syntax is close enough
to
other languages. There will be no barrier for our users in the long run.
This argument is irrelevant for several reasons:
(1) I am not familiar with any of those languages, nor are many PHP users.
(2) Just because other languages have such a feature is not a good reason
for adding it into PHP.
(3) Introducing write-only (less readable or completely unreadable) code
will ALWAYS be a barrier to those who have used nothing but PHP for the last
10+ years.
As H. Abelson and G. Sussman wrote in 1984: "Programs must be written for
people to read, and only incidentally for machines to execute." Writing
compact code which has several functions compressed into a single line may
be clever where you come from, but it leaves new readers scratching their
heads saying "WTF!!".
As a follower of the KISS principle I always aim to write simple, readable
code, and my critics always say "It is too simple. Proper OO is supposed to
be more complex than that". The mark of genius is to achieve complex things
in a simple manner, not to achieve simple things in a complex manner.
--
Tony Marston
This argument is irrelevant for several reasons:
(1) I am not familiar with any of those languages, nor are many PHP users.
(2) Just because other languages have such a feature is not a good reason
for adding it into PHP.
(3) Introducing write-only (less readable or completely unreadable) code
will ALWAYS be a barrier to those who have used nothing but PHP for the last
10+ years.As H. Abelson and G. Sussman wrote in 1984: "Programs must be written for
people to read, and only incidentally for machines to execute." Writing
compact code which has several functions compressed into a single line may
be clever where you come from, but it leaves new readers scratching their
heads saying "WTF!!".As a follower of the KISS principle I always aim to write simple, readable
code, and my critics always say "It is too simple. Proper OO is supposed to
be more complex than that". The mark of genius is to achieve complex things
in a simple manner, not to achieve simple things in a complex manner.
The thing is, every feature and syntax can be used to create some
unreadable "monstrosity". And often, at the same time, they can be
used to create readable elegant code. :)
Sure, "elegant" and "readable" is subjective, and for someone who
never heard of FP, this might be befuddling. But so is probably the
entire concept of anonymous functions and high order functions. Does
it mean PHP shouldn't support passing functions to functions at all? I
think not.
Also, once you know ~> is a syntax sugar for anonymous functions (and
understand them), I don't think there's any general readability
problem, and in some cases, it makes the code even more readable
(subjective experience from other languages). And if the programmer
doesn't understand the concept of anonymous functions at all, he will
be lost anyways and shorter or longer syntax doesn't matter.
I personally wouldn't even mind just having a version that supports
[variables] ~> [single expression], but since other languages support
having whole function body with return statement, I think it's worth
considering adding it too.
Regards
Pavel Kouril
Anthony Ferrara wrote on 01/09/2015 06:53:
function partial(callable $cb) {
return $left ~> $right ~> $cb($left, $right);
}
The thing that is most unreadable to me in this is the associativity.
Someone from, say, a Haskell background might be used to mentally
grouping X -> Y -> Z into a series of steps but to an untrained eye it
looks almost like there's a list of terms with ~> as the separator. As I
understand it, an expanded form would be:
return ($left) ~> { return ($right) ~> { return $cb($left, $right) } };
...which is a bit easier to mentally group, but not much prettier than
current PHP syntax.
What about something a bit more "enclosed" but still short. A
for-loop-style construct occured to me:
lambda(params; expression)
lambda($a, $b; $a * $b)
lambda($x; { while(foo()) { $x ++; } return $x; })
The curly braces don't look very natural in that last example, but I'm
not sure what else to use. Maybe just use the existing function() syntax
if you want a block rather than an expression?
A nice expansion of this would be to have an explicit use clause without
any extra keywords:
lambda(params; bound vars; expression)
$double = lambda($a;; 2*$a)
$x=3; $triple = lambda($a; $x; $x * $a)
Taking one of the RFC's examples:
function sumEventScores($events, $scores) {
$types = array_map(lambda($event;; $event['type']), $events);
return array_reduce($types, lambda($sum, $type; $scores; $sum +
$scores[$type]));
}
The next example benefits less if we allow only expressions, but you
could still write this:
function reduce(callable $fn) {
return lambda($initial; $fn;
function($input) use ($fn, $initial) {
$accumulator = $initial;
foreach ($input as $value) {
$accumulator = $fn($accumulator, $value);
}
return $accumulator;
}
);
}
I'll leave it to others to judge if this is better or worse than the
current proposal, just playing with ideas...
Regards,
Rowan Collins
[IMSoP]
- aug. 31. 21:29 ezt írta ("Bob Weinand" bobwei9@hotmail.com):
I had this RFC in draft since some time, but delayed it due to all the
ongoing PHP 7 discussions. Also we have no master branch to merge features
in until 5.4 EOL. Thus I'm reviving this now.Time for the first RFC targeting PHP 7.1 (assuming PHP 8 isn't going to
be the next version ;-)):The short Closures RFC:
https://wiki.php.net/rfc/short_closuresHoping for constructive feedback,
Bob
+1 for having short closure operator, -1 for autocopying everything from
the current scope.
I had this RFC in draft since some time, but delayed it due to all the ongoing PHP 7 discussions. Also we have no master branch to merge features in until 5.4 EOL. Thus I'm reviving this now.
Time for the first RFC targeting PHP 7.1 (assuming PHP 8 isn't going to be the next version ;-)):
The short Closures RFC:
https://wiki.php.net/rfc/short_closuresHoping for constructive feedback,
Bob
Hi,
as a purely userland developer, I would definitely appreciate the
shorthand function for anonymous functions; having tons of stuff like
"function ($x) { return $x * 2; }" makes the code less readable in
the end.
I'm not sure about the "auto using" of all variables though; wouldnt
it be possible to statically check for the used variables and only
import what's needed, for performance reasons?
Also, how hard would it be to add type hints (only for parameters)?
Sometimes they are needed to make the IDE know the variable type
because it can't be guessed automatically. I know about your note in
RFC,this is just a question to other internal members.Return type can
be infered by IDEs from the simple expresion quite easily.
PS: would "() ~> foo()" work? I think it should, but I couldn't find a
mention about it in RFC. :)
Regards
Pavel Kouril
Pavel
I had this RFC in draft since some time, but delayed it due to all the ongoing PHP 7 discussions. Also we have no master branch to merge features in until 5.4 EOL. Thus I'm reviving this now.
Time for the first RFC targeting PHP 7.1 (assuming PHP 8 isn't going to be the next version ;-)):
The short Closures RFC:
https://wiki.php.net/rfc/short_closuresHoping for constructive feedback,
BobHi,
as a purely userland developer, I would definitely appreciate the
shorthand function for anonymous functions; having tons of stuff like
"function ($x) { return $x * 2; }" makes the code less readable in
the end.I'm not sure about the "auto using" of all variables though; wouldnt
it be possible to statically check for the used variables and only
import what's needed, for performance reasons?
That's precisely what's happening. Not all variables are bound, only
those that are used. Somehow somewhere in this thread the confusion
was implied that the entire scope is copied.
Also, how hard would it be to add type hints (only for parameters)?
Sometimes they are needed to make the IDE know the variable type
because it can't be guessed automatically. I know about your note in
RFC,this is just a question to other internal members.Return type can
be infered by IDEs from the simple expresion quite easily.PS: would "() ~> foo()" work? I think it should, but I couldn't find a
mention about it in RFC. :)
Typing on closures is outside the scope of this RFC.
With that said, I'd love to hear and see examples of this. It's
something I definitely want to do, just haven't come up with a good
enough way to do it...
Anthony
I'm not sure about the "auto using" of all variables though; wouldnt
it be possible to statically check for the used variables and only
import what's needed, for performance reasons?That's precisely what's happening. Not all variables are bound, only
those that are used. Somehow somewhere in this thread the confusion
was implied that the entire scope is copied.
Oh, nice - thanks for explaining that. :)
Also, how hard would it be to add type hints (only for parameters)?
Sometimes they are needed to make the IDE know the variable type
because it can't be guessed automatically. I know about your note in
RFC,this is just a question to other internal members.Return type can
be infered by IDEs from the simple expresion quite easily.PS: would "() ~> foo()" work? I think it should, but I couldn't find a
mention about it in RFC. :)Typing on closures is outside the scope of this RFC.
With that said, I'd love to hear and see examples of this. It's
something I definitely want to do, just haven't come up with a good
enough way to do it...
Yeah, I know it's outside of the scope of the RFC, it was more of a
general question whether or not it would be possible in the future.
Also, exactly what examples would you want to see?
Pavel
Dear all,
Just to throw in my 2 cents as a userland developer: having multiple
ways to write the same thing isnt always right, but does have
advantages. (i.e. the [] shorthand is gaining alot of popularity as
already noted) (and might even force array() into deprecation imho)
But it does also add clutter since a developer has to have more
syntax knowledge in order to read the code. In this case, chaining
alot of short hand closures would require a bit of a learning curve
where the function() syntax is already everywhere... Reading the
functions is more self-explanatory...
But anyway, with regard to the type hints and such: I saw the [draft]
Callable-types RFC ( https://wiki.php.net/rfc/callable-types ) which
before this discussion really sparked my interest of having types
everywhere (yeeeey!) and with this shorthand closure RFC the two
might even get all the FP guys back into PHP ;)
In anycase, Anthony, you might want to have a look at the draft RFC,
since from a userland view, this seems a really good way to go.
Good luck with this development people! Looking forward to whatever
comes out of it!
Met vriendelijke groet,
Robin Speekenbrink
Kingsquare BV
2015-09-01 10:44 GMT+02:00 Anthony Ferrara ircmaxell@gmail.com:
Pavel
I had this RFC in draft since some time, but delayed it due to all the ongoing PHP 7 discussions. Also we have no master branch to merge features in until 5.4 EOL. Thus I'm reviving this now.
Time for the first RFC targeting PHP 7.1 (assuming PHP 8 isn't going to be the next version ;-)):
The short Closures RFC:
https://wiki.php.net/rfc/short_closuresHoping for constructive feedback,
BobHi,
as a purely userland developer, I would definitely appreciate the
shorthand function for anonymous functions; having tons of stuff like
"function ($x) { return $x * 2; }" makes the code less readable in
the end.I'm not sure about the "auto using" of all variables though; wouldnt
it be possible to statically check for the used variables and only
import what's needed, for performance reasons?That's precisely what's happening. Not all variables are bound, only
those that are used. Somehow somewhere in this thread the confusion
was implied that the entire scope is copied.Also, how hard would it be to add type hints (only for parameters)?
Sometimes they are needed to make the IDE know the variable type
because it can't be guessed automatically. I know about your note in
RFC,this is just a question to other internal members.Return type can
be infered by IDEs from the simple expresion quite easily.PS: would "() ~> foo()" work? I think it should, but I couldn't find a
mention about it in RFC. :)Typing on closures is outside the scope of this RFC.
With that said, I'd love to hear and see examples of this. It's
something I definitely want to do, just haven't come up with a good
enough way to do it...Anthony
I had this RFC in draft since some time, but delayed it due to all the ongoing PHP 7 discussions. Also we have no master branch to merge features in until 5.4 EOL. Thus I'm reviving this now.
Time for the first RFC targeting PHP 7.1 (assuming PHP 8 isn't going to be the next version ;-)):
The short Closures RFC:
https://wiki.php.net/rfc/short_closures
The RFC writes:
The symbol ~> was chosen as it is a mnemonic device to help
programmers understand that the variable is being brought to a
function.
I don't see how this is a mnemonic. I am also struggeling seeing the
difference between -> and ~> in certain fonts.
Currently Hack has implemented shorthand anonymous functions using the
==> symbol to define them.
Additionally, ==> in Hack has slightly different semantics, hence we
decided, it's better to not reuse that symbol
What are the difference in semantics?
Why not just copy the Hack semantics (and hence, then also use it's symbol)?
cheers,
Derick
Hi Bob,
I had this RFC in draft since some time, but delayed it due to all the ongoing PHP 7 discussions. Also we have no master branch to merge features in until 5.4 EOL. Thus I'm reviving this now.
Time for the first RFC targeting PHP 7.1 (assuming PHP 8 isn't going to be the next version ;-)):
The short Closures RFC:
https://wiki.php.net/rfc/short_closuresHoping for constructive feedback,
Bob
Whilst I'd like to see a more expressive syntax for closures (and many other
things) in PHP, I'm personally -1 on your proposal. The feature feels too
inconsistent with the syntax and semantics of current PHP constructs for the
following reasons:
- The automatic binding of variables from the outer scope to the inner scope.
- The optional use of parentheses for parameter definitions, but only when a
single argument is used. - The optional use of braces for the function body, but only when a single
expression is used. - The automatic returning of the expression, but only when it is alone, and
only if the braces are omitted.
These four differences create further special cases for people to learn and
have no precedence in the language at all. They seem more like Rubyisms than
PHPisms.
Whilst we're on the topic of a terser syntax for closures though, Elixir has an
even shorter syntax:
&(&1 + &2)
The &() denotes an closure definition; the &N is used to create placeholders,
where N corresponds to the argument number. The ampersand obviously cannot be
reused for this in PHP, but the dollar sign could:
$func = $($1 + $2);
var_dump($func(3, 4)); // int(7)
This syntax should only be used in trivial scenarios, though, and comes with
the disadvantage of no type information.
Thanks,
Tom
The short Closures RFC:
https://wiki.php.net/rfc/short_closures
I just want to toss in my two cents as someone who's been using short
closures on a PHPish platform for some time already, because
predictions are one thing, experience in another.
The first time I saw "==>" I was like "what the eff?", but then you
know what I did? I looked up what it did, and I haven't had a problem
reading it since. In fact, I have a much easier time reading a short
lambda than an over verbose function declaration sitting in the middle
of another expression.
I don't think PHP needs to adopt every feature that the majority of
the most used languages have chosen to implement, but this one should
really be a no-brainer, regardless of which token is used. Because
code-beauty does matter. Beautiful code is code that is easier to
maintain, and code that is easier to maintain is often more secure and
reliable.
That's all.
-Sara
Beautiful code is code that is easier to
maintain, and code that is easier to maintain is often more secure and
reliable.
I think this is an important point. Obviously, "beauty" is subjective:
some people will find very verbose, English-like code easier to read;
others might try to squeeze as much information into one screenful as
possible, so they can see the whole program at a glance.
At one extreme, SQL has mandatory words like the "AS" in "CAST(x AS
int)" just to "look more like English"; at the other, APL has symbols
all of its own with their own Unicode code points. Most languages aim
for some compromise in between.
So I would like to put forward for consideration these amendments to the
proposal, in the spirit of compromise (in no particular order; numbers
are just for reference in future discussion):
Amendment 1. Only allow the single-expression form of the short syntax;
full function bodies must be declared with the existing function(){ ...
} syntax. This gives a clearer distinction between how the two syntaxes
can be used.
Amendment 2. Make the ~> operator non-associative, so that lambdas
returning lambdas would need an extra pair of brackets making the
nesting explicit. To take one of Anthony's examples:
function partial(callable $cb) {
return $left ~> ( $right ~> $cb($left, $right) );
}
It's now at least clearer that there are two different anonymous functions here, even if it's not entirely clear what they're trying to achieve...
Amendment 3. Make the parentheses around the argument list mandatory so
that (if Amendment 1 is also adopted) there is only a single variant of
the syntax, namely "( param list ) ~> expression".
Amendment 4. Consider a syntax involving an explicit "use". This one
requires a bit more exploration, but maybe something like:
( $a use $b ) ~> $a + $b
or:
( $a ) use ( $b ) ~> $a + $b
Perhaps there are other thoughts along these lines that could move the
proposal beyond a simple love/hate debate.
Regards,
--
Rowan Collins
[IMSoP]
So I would like to put forward for consideration these amendments to the
proposal, in the spirit of compromise (in no particular order; numbers are
just for reference in future discussion):Amendment 1. Only allow the single-expression form of the short syntax; full
function bodies must be declared with the existing function(){ ... } syntax.
This gives a clearer distinction between how the two syntaxes can be used.
This one sounds reasonable, but you'll probably get a RFC for almost
every upcoming version to allow the more complex bodies.
Amendment 2. Make the ~> operator non-associative, so that lambdas returning
lambdas would need an extra pair of brackets making the nesting explicit. To
take one of Anthony's examples:function partial(callable $cb) {
return $left ~> ( $right ~> $cb($left, $right) );
}It's now at least clearer that there are two different anonymous functions
here, even if it's not entirely clear what they're trying to achieve...
Why? Once you read documentation about how the operator works and what
it does, you will know it and writing the extra brackets seems
superfluos. And woudn't this complicate the parser even more? Also, if
you have no idea about functional programming whatsoever, this will be
hard to grasp in any syntax form, but the syntax isn't the problem in
that case (as I already said in previous email).
Amendment 3. Make the parentheses around the argument list mandatory so that
(if Amendment 1 is also adopted) there is only a single variant of the
syntax, namely "( param list ) ~> expression".Amendment 4. Consider a syntax involving an explicit "use". This one
requires a bit more exploration, but maybe something like:
( $a use $b ) ~> $a + $b
or:
( $a ) use ( $b ) ~> $a + $b
So you are taking a syntax that's supposed to be short and "clean" and
making it more complex by the amedments 3 and 4? I wouldn't mind the
mandatory ( ) around parameters, but at the same time I think that
it's an useless rule and makes the written code more "cluttered" (too
much ( and ) imho make the code less readable).
Btw. about your previous email and this syntax ending your career -
why would it? Reading the documentation and doing a mental note of
"simplified anonymous function declaration with optional ( ) if
there's just one paramater and automatically returning the expression
or having a whole function body enclosed in { }" shouldn't be a
problem? This is definitely not a "write-only" code.
If you are afraid the syntax will make the code less readable - look
at other languages (not for the sake of "they do it too", but for "how
does the code using it look like"). I can only speak about C#, and it
feels like it fits there - even though the language has more verbose
constructs and stuff. This should IMHO be a natural fit for PHP.
Regards
Pavel Kouril
Pavel Kouřil wrote on 03/09/2015 07:32:
Amendment 2. Make the ~> operator non-associative
Why? Once you read documentation about how the operator works and what
it does, you will know it and writing the extra brackets seems
superfluos. And woudn't this complicate the parser even more? Also, if
you have no idea about functional programming whatsoever, this will be
hard to grasp in any syntax form, but the syntax isn't the problem in
that case (as I already said in previous email).
Yeah, maybe this one is just me - I just find right-associativity really
hard to read, whatever it's purpose. Maybe it's because I've only ever
read left-to-right languages.
As mentioned in a previous e-mail, when I look at "$a ~> $b ~> $c" it
makes me think of a single chain where all the terms are somehow
related, like "$a = $b = $c", rather than any kind of nesting. Maybe I'd
get used to it, maybe it would be rare enough that I'd never need to.
So you are taking a syntax that's supposed to be short and "clean" and
making it more complex by the amedments 3 and 4?
No, I'm trying to think of compromises which address constructive
criticisms people have made of the current proposal, such as the variety
of syntax forms, and the implicit scoping of variables.
To be clear, these are not demands or anything like that, just
suggestions of how to move the discussion beyond "here's the proposal,
take it or leave it".
Btw. about your previous email and this syntax ending your career -
why would it?
That wasn't my e-mail, it was one I replied to. I'm not against this
proposal, I'm genuinely trying to find ways forward with it.
Regards,
Rowan Collins
[IMSoP]
Den 2015-09-03 kl. 11:56, skrev Rowan Collins:
Pavel Kouřil wrote on 03/09/2015 07:32:
Amendment 2. Make the ~> operator non-associative
Why? Once you read documentation about how the operator works and what
it does, you will know it and writing the extra brackets seems
superfluos. And woudn't this complicate the parser even more? Also, if
you have no idea about functional programming whatsoever, this will be
hard to grasp in any syntax form, but the syntax isn't the problem in
that case (as I already said in previous email).Yeah, maybe this one is just me - I just find right-associativity
really hard to read, whatever it's purpose. Maybe it's because I've
only ever read left-to-right languages.As mentioned in a previous e-mail, when I look at "$a ~> $b ~> $c" it
makes me think of a single chain where all the terms are somehow
related, like "$a = $b = $c", rather than any kind of nesting. Maybe
I'd get used to it, maybe it would be rare enough that I'd never need to.So you are taking a syntax that's supposed to be short and "clean" and
making it more complex by the amedments 3 and 4?No, I'm trying to think of compromises which address constructive
criticisms people have made of the current proposal, such as the
variety of syntax forms, and the implicit scoping of variables.To be clear, these are not demands or anything like that, just
suggestions of how to move the discussion beyond "here's the proposal,
take it or leave it".
Really appreciate that! Like the idea but the variation in syntax forms
is a bit confusing. Maybe one start with a very simple function and as
as you expand it the syntax form needs to change which I think is a
drawback.
Regards , //Björn Larsson
Pavel Kouřil wrote on 03/09/2015 07:32:
Amendment 2. Make the ~> operator non-associative
Why? Once you read documentation about how the operator works and what
it does, you will know it and writing the extra brackets seems
superfluos. And woudn't this complicate the parser even more? Also, if
you have no idea about functional programming whatsoever, this will be
hard to grasp in any syntax form, but the syntax isn't the problem in
that case (as I already said in previous email).Yeah, maybe this one is just me - I just find right-associativity really
hard to read, whatever it's purpose. Maybe it's because I've only ever read
left-to-right languages.As mentioned in a previous e-mail, when I look at "$a ~> $b ~> $c" it makes
me think of a single chain where all the terms are somehow related, like "$a
= $b = $c", rather than any kind of nesting. Maybe I'd get used to it, maybe
it would be rare enough that I'd never need to.
Yeah, my guess is that most developers would use this just for more
simple array_map/array_filter/etc. usage, so you probably wouldn't
encounter this use case "in the wild" much.
But imagine having something like this (combined with your #3):
function partial($cb) { return ($left) ~> (($right) ~> $cb($left, $right)); }
I hope I got the parenthesination correct, because now there's a ton
of parenthesis and it's even harder to read (and write). If I had to
choose between amendments #3 and #2, I'd go with #3 just for the sake
of consistency - but I definitely wouldn't choose both of them
simultaneously.
But even just #3 seems kinda "harder" to read than the form without
any parenthesis.
function partial($cb) { return ($left) ~> ($right) ~> $cb($left, $right); }
I know the parenthesis are optional in just one scenario, but I'd
argue it's probably the most common one.
So you are taking a syntax that's supposed to be short and "clean" and
making it more complex by the amedments 3 and 4?No, I'm trying to think of compromises which address constructive criticisms
people have made of the current proposal, such as the variety of syntax
forms, and the implicit scoping of variables.To be clear, these are not demands or anything like that, just suggestions
of how to move the discussion beyond "here's the proposal, take it or leave
it".
Yes, I hope everyone is trying to move the discussion forwards,
because I would love to see this in some reasonable form in PHP.
In my personal order of "most reasonable" ammendments, it would be #1
#3 > #2 > #4. The #1 I could even see getting positive feedback,
because for everything longer than one statement, people can use the
function () { ... } declaration (unlike in some other languages).
Btw. about your previous email and this syntax ending your career -
why would it?That wasn't my e-mail, it was one I replied to. I'm not against this
proposal, I'm genuinely trying to find ways forward with it.
Oh, really sorry! I read a lot of those emails at the same time and
somehow got two emails mixed up together! :)
Regards
PK
snip
But even just #3 seems kinda "harder" to read than the form without
any parenthesis.function partial($cb) { return ($left) ~> ($right) ~> $cb($left, $right); }
I know the parenthesis are optional in just one scenario, but I'd
argue it's probably the most common one.
You're arguing that, subjectively, to you - parentheses make things harder
to read. For others they clarify things.
It should be obvious to everyone that this particular path of the
discussion has about as much merit as tabs vs spaces.
That being the case, I would argue for consistency and simplicity. If you
need parentheses for other variants of this, require parentheses all the
way through. It will be simpler to learn and trip fewer people up.
Just think, if whoever constructed the if conditional hadnt thought "hey,
let's be clever and save some keystrokes by making the curlies optional in
some cases" we wouldn't have the multitude of bugs and security holes we
know to exist, we wouldn't have to warn the young'uns against improper use
of if, we wouldn't have to write codesniffer rules against single line ifs,
etc, etc.
Any argument to the effect of "let's be clever" or "it'll save some
keystrokes" is void. Period.
Regards
Peter
--
<hype>
WWW: plphp.dk / plind.dk
CV: careers.stackoverflow.com/peterlind
LinkedIn: plind
Twitter: kafe15
</hype
You're arguing that, subjectively, to you - parentheses make things harder
to read. For others they clarify things.
It should be obvious to everyone that this particular path of the discussion
has about as much merit as tabs vs spaces.
Sure, it is subjective - but what isn't?
That being the case, I would argue for consistency and simplicity. If you
need parentheses for other variants of this, require parentheses all the way
through. It will be simpler to learn and trip fewer people up.
Depends how you define simplicity. Because $a ~> $b ~> $c ~> $d is
IMHO more simple than ($a) ~> ((($b) ~> (($c) ~> $d($foo))) - which is
a result of the combination of amendments #2 and #3. I honestly do not
know if I wrote the parenthesis right now or not (probably not),
because there's simply just too many of them.
Sure, parenthesis can help people understand things, but I'd say that
at the same time, too many of them can be a problem as well (as the
fun name for LISP - "lost in stupid parenthesis" - suggests).
Just think, if whoever constructed the if conditional hadnt thought "hey,
let's be clever and save some keystrokes by making the curlies optional in
some cases" we wouldn't have the multitude of bugs and security holes we
know to exist, we wouldn't have to warn the young'uns against improper use
of if, we wouldn't have to write codesniffer rules against single line ifs,
etc, etc.Any argument to the effect of "let's be clever" or "it'll save some
keystrokes" is void. Period.
This is not about saving characters, it's about not overcomplicating things.
Regards
Peter
Regards
PK
You're arguing that, subjectively, to you - parentheses make things
harder
to read. For others they clarify things.
It should be obvious to everyone that this particular path of the
discussion
has about as much merit as tabs vs spaces.Sure, it is subjective - but what isn't?
Consistency isn't.
That being the case, I would argue for consistency and simplicity. If you
need parentheses for other variants of this, require parentheses all the
way
through. It will be simpler to learn and trip fewer people up.Depends how you define simplicity. Because $a ~> $b ~> $c ~> $d is
IMHO more simple than ($a) ~> ((($b) ~> (($c) ~> $d($foo))) - which is
a result of the combination of amendments #2 and #3. I honestly do not
know if I wrote the parenthesis right now or not (probably not),
because there's simply just too many of them.Sure, parenthesis can help people understand things, but I'd say that
at the same time, too many of them can be a problem as well (as the
fun name for LISP - "lost in stupid parenthesis" - suggests).
Is it? And are the examples you make use of the only two options?
What about requiring parentheses around arguments, but not around the
function body? That would guard against stupid "I'll add another argument
... why isn't it working?!?!?!" errors (getting a syntax error and then
having to fix your code isn't easier than just consistently adding
parentheses around arguments).
Just think, if whoever constructed the if conditional hadnt thought "hey,
let's be clever and save some keystrokes by making the curlies optional
in
some cases" we wouldn't have the multitude of bugs and security holes we
know to exist, we wouldn't have to warn the young'uns against improper
use
of if, we wouldn't have to write codesniffer rules against single line
ifs,
etc, etc.Any argument to the effect of "let's be clever" or "it'll save some
keystrokes" is void. Period.This is not about saving characters, it's about not overcomplicating
things.
You're aiming for the "let's be clever" camp, far as I can tell.
--
<hype>
WWW: plphp.dk / plind.dk
CV: careers.stackoverflow.com/peterlind
LinkedIn: plind
Twitter: kafe15
</hype
Depends how you define simplicity. Because $a ~> $b ~> $c ~> $d is
IMHO more simple than ($a) ~> ((($b) ~> (($c) ~> $d($foo))) - which is
a result of the combination of amendments #2 and #3. I honestly do not
know if I wrote the parenthesis right now or not (probably not),
because there's simply just too many of them.
Might be a good place to suggest refactoring?
Removing a semantics because when you try to write too dense code it's hard
to read.
The same goes with an one line if assignments.
$a = ( [some really big expression] ) ? $b : $c;
Could be really hard to read but then you look into refactoring that code.
Might be my inexperience with closures but when do you chain 3 of them?
Best regards
Daniel
You're arguing that, subjectively, to you - parentheses make things
harder
to read. For others they clarify things.
It should be obvious to everyone that this particular path of the
discussion
has about as much merit as tabs vs spaces.Sure, it is subjective - but what isn't?
That being the case, I would argue for consistency and simplicity. If you
need parentheses for other variants of this, require parentheses all the
way
through. It will be simpler to learn and trip fewer people up.Depends how you define simplicity. Because $a ~> $b ~> $c ~> $d is
IMHO more simple than ($a) ~> ((($b) ~> (($c) ~> $d($foo))) - which is
a result of the combination of amendments #2 and #3. I honestly do not
know if I wrote the parenthesis right now or not (probably not),
because there's simply just too many of them.Sure, parenthesis can help people understand things, but I'd say that
at the same time, too many of them can be a problem as well (as the
fun name for LISP - "lost in stupid parenthesis" - suggests).Just think, if whoever constructed the if conditional hadnt thought "hey,
let's be clever and save some keystrokes by making the curlies optional
in
some cases" we wouldn't have the multitude of bugs and security holes we
know to exist, we wouldn't have to warn the young'uns against improper
use
of if, we wouldn't have to write codesniffer rules against single line
ifs,
etc, etc.Any argument to the effect of "let's be clever" or "it'll save some
keystrokes" is void. Period.This is not about saving characters, it's about not overcomplicating
things.Regards
PeterRegards
PK
(This post is fairly lengthy so I have copied it into a gist if you
would prefer to read it that way:
https://gist.github.com/morrisonlevi/8323125182ca3b5e2124)
Here's a practical example of chaining closures together that allows
us to easily implement versions of array_map
and array_reduce
that
are highly generic, meaning they can be used with various kinds of
input and output types instead of just arrays. These new versions can
also easily be chained together to form a "pipe" of transformations.
While the short-form closures are not required for this to work it
does make it much simpler.
Also, I know this is a fair bit of code to dump at once; it's really
hard to give meaningful examples that don't have some length to them.
Please don't skip this code!
$map = $fn ~> $input ~> {
foreach ($input as $key => $value) {
yield $key => $fn($value);
}
};
$reduce = $initial ~> $fn ~> $input ~> {
$accumulator = $initial;
foreach ($input as $value) {
$accumulator = $fn($accumulator, $value);
}
return $accumulator;
};
// this is just a helper function
// can skip reading it if you want
function chain(callable $fn, callable ...$callables) {
$functions = `func_get_args()`;
return (...$params) ~> {
$count = count($functions);
$carry = $functions[0](...$params);
for ($i = 1; $i < $count; $i++) {
$carry = $functions[$i]($carry);
}
return $carry;
};
}
This essentially allows for binding only certain parameters and
chaining whole algorithms together:
$algorithm = chain(
$map($x ~> $x * 2),
$reduce([])(function ($accumulator, $value) {
$accumulator[] = $value;
return $accumulator;
})
);
$result = $algorithm($iterable);
Note that while I've used a basic array here for brevity I could have
used any kind of container that supports adding single items. For
instance, I could have used a set:
$set = new Set();
$algorithm = chain(
$map($x ~> $x * 2),
$reduce($set)(function ($set, $value) {
$set->add($value)
return $set;
})
);
$result = $algorithm($iterable);
Now all the duplicates get removed should there be any.
Hopefully I've been able to demonstrate that this style of coding is
powerful and that the chaining of closures was helpful. Here are the
long-form closure equivalents for contrast:
$map = function ($fn) {
return function($input) use($fn) {
foreach ($input as $key => $value) {
yield $key => $fn($value);
}
};
};
$reduce = function ($initial) {
return function ($fn) use($initial) {
return function($input) use($initial, $fn) {
$accumulator = $initial;
foreach ($input as $value) {
$accumulator = $fn($accumulator, $value);
}
return $accumulator;
};
};
};
function chain(callable $fn, callable ...$callables) {
$functions = `func_get_args()`;
return function(...$params) use($functions) {
$count = count($functions);
$carry = $functions[0](...$params);
for ($i = 1; $i < $count; $i++) {
$carry = $functions[$i]($carry);
}
return $carry;
};
}
$algorithm = chain(
$map(function ($x) {
return $x * 2;
}),
$reduce([])(function ($accumulator, $value) {
$accumulator[] = $value;
return $accumulator;
})
);
Some people have also suggested removing the block syntax for short
closures. The implementation of reduce as defined above is a
demonstration of why the block syntax is helpful:
$reduce = $initial ~> $fn ~> $input ~> {
$accumulator = $initial;
foreach ($input as $value) {
$accumulator = $fn($accumulator, $value);
}
return $accumulator;
};
With the block syntax removed the last closure in the chain has to use
long-form like this:
$reduce = $initial ~> $fn ~> function($input) use($initial, $fn) {
$accumulator = $initial;
foreach ($input as $value) {
$accumulator = $fn($accumulator, $value);
}
return $accumulator;
};
I hope you'll agree with me that this is just weird. I was initially
against the block syntax but after using it in meaningful code I
concluded that the block syntax is helpful.
Levi Morrison wrote on 04/09/2015 18:34:
Hopefully I've been able to demonstrate that this style of coding is
powerful and that the chaining of closures was helpful.
Sort of - the chaining is far from self-explanatory, and some of it is
still rather artificial. For instance, surely map would in reality have
been a named function:
function map($fn) {
return $input ~> {
foreach ($input as $key => $value) {
yield $key => $fn($value);
}
};
Reduce can also be simplified to a named function and a single closure:
function reduce($initial, $fn)
return $input ~> {
$accumulator = $initial;
foreach ($input as $value) {
$accumulator = $fn($accumulator, $value);
}
return $accumulator;
};
}
I realise you never claimed these functions were as simple as possible,
or a perfect example of using higher-order functions, but right now I
can't see why you'd write code like that other than as a coding exercise.
Some people have also suggested removing the block syntax for short
closures. The implementation of reduce as defined above is a
demonstration of why the block syntax is helpful:$reduce = $initial ~> $fn ~> $input ~> { $accumulator = $initial; foreach ($input as $value) { $accumulator = $fn($accumulator, $value); } return $accumulator; };
With the block syntax removed the last closure in the chain has to use
long-form like this:$reduce = $initial ~> $fn ~> function($input) use($initial, $fn) { $accumulator = $initial; foreach ($input as $value) { $accumulator = $fn($accumulator, $value); } return $accumulator; };
I hope you'll agree with me that this is just weird.
Not really, no. The difference in behaviour of variable scope makes the
use() statement look out of place, but the fact that there are two
trivial functions and one complex one, and therefore two different
pieces of syntax, seems perfectly reasonable.
Regards,
Rowan Collins
[IMSoP]
Levi Morrison wrote on 04/09/2015 18:34:
Hopefully I've been able to demonstrate that this style of coding is
powerful and that the chaining of closures was helpful.Sort of - the chaining is far from self-explanatory, and some of it is still
rather artificial. For instance, surely map would in reality have been a
named function:function map($fn) {
return $input ~> {
foreach ($input as $key => $value) {
yield $key => $fn($value);
}
};
It's really bothering me that you have a mismatched curly brace. :D
Reduce can also be simplified to a named function and a single closure:
function reduce($initial, $fn)
return $input ~> {
$accumulator = $initial;
foreach ($input as $value) {
$accumulator = $fn($accumulator, $value);
}
return $accumulator;
};
}I realise you never claimed these functions were as simple as possible, or a
perfect example of using higher-order functions, but right now I can't see
why you'd write code like that other than as a coding exercise.
Sure, I didn't defend the choices too much because the post was
already lengthy. If you care only that the last argument in the chain
is the input then your definition would be just fine. You are right
that these functions would likely be named, but if we keep the
currying pattern the short-form closures are still helpful:
function reduce($initial) {
return $fn ~> $input ~> {
$accumulator = $initial;
foreach ($input as $value) {
$accumulator = $fn($accumulator, $value);
}
return $accumulator;
};
}
If this RFC passes I'd like to see one that builds on it to remove the
{ return <expr> ;} boilerplate of the above definition:
function reduce ($initial) ~> $fn ~> $input {
$accumulator = $initial;
foreach ($input as $value) {
$accumulator = $fn($accumulator, $value);
}
return $accumulator;
}
I'm not saying it would be that syntax exactly but something like that
would be nice if this RFC passes.
Some people have also suggested removing the block syntax for short
closures. The implementation of reduce as defined above is a
demonstration of why the block syntax is helpful:$reduce = $initial ~> $fn ~> $input ~> { $accumulator = $initial; foreach ($input as $value) { $accumulator = $fn($accumulator, $value); } return $accumulator; };
With the block syntax removed the last closure in the chain has to use
long-form like this:$reduce = $initial ~> $fn ~> function($input) use($initial, $fn) { $accumulator = $initial; foreach ($input as $value) { $accumulator = $fn($accumulator, $value); } return $accumulator; };
I hope you'll agree with me that this is just weird.
Not really, no. The difference in behaviour of variable scope makes the
use() statement look out of place, but the fact that there are two trivial
functions and one complex one, and therefore two different pieces of syntax,
seems perfectly reasonable.
I guess we just disagree there. I have no further comment on it.
Levi Morrison wrote on 07/09/2015 17:56:
Sort of - the chaining is far from self-explanatory, and some of it is still
rather artificial. For instance, surely map would in reality have been a
named function:function map($fn) {
return $input ~> {
foreach ($input as $key => $value) {
yield $key => $fn($value);
}
};
It's really bothering me that you have a mismatched curly brace. :D
Oops, sorry!
Here you go:
}
:)
Levi Morrison wrote on 07/09/2015 17:56:
function reduce ($initial) ~> $fn ~> $input ~> { $accumulator = $initial; foreach ($input as $value) { $accumulator = $fn($accumulator, $value); } return $accumulator; }
Hm, this is an interesting idea. I actually think the case for it is
stronger if ~> was considered special syntax for trivial functions,
because if not, there's this odd hybrid, which we probably wouldn't want
to allow:
function weird_declaration($bar) ~> {
any();
code();
here();
}
Whereas this declaration might be quite useful:
function double($x) ~> $x * 2;
I realise the syntax was only an example, but this way there'd be a nice
consistency between named and anonymous functions.
I think that would leave your example as:
function reduce ($initial) ~> $fn ~> function($input) use ( $initial, $fn) {
$accumulator = $initial;
foreach ($input as $value) {
$accumulator = $fn($accumulator, $value);
}
return $accumulator;
}
Regards,
Rowan Collins
[IMSoP]
Hi Levi,
Levi Morrison wrote:
If this RFC passes I'd like to see one that builds on it to remove the
{ return <expr> ;} boilerplate of the above definition:function reduce ($initial) ~> $fn ~> $input { $accumulator = $initial; foreach ($input as $value) { $accumulator = $fn($accumulator, $value); } return $accumulator; }
I'm not saying it would be that syntax exactly but something like that
would be nice if this RFC passes.
That looks rather weird to me, but I like the concept.
I think a possible improvement might be a generalised syntax, similar to
that used for constants, that lets you use any \Closure object to define
a function or method. Thus:
function reduce = $initial ~> $fn ~> $input ~> {
// ...
};
This has that advantage that it isn't just useful for when you want to
use the short syntax. You can also use it when you're obtaining a
Closure in some novel way, perhaps a higher-order function:
// where "add" is a function that adds two numbers
function sum = reduce(0)("add");
With this, you don't have to write a wrapper function, or use a global
variable, to define a proper top-level function.
Hope this is helpful.
--
Andrea Faulds
http://ajf.me/
I think a possible improvement might be a generalised syntax, similar to
that used for constants, that lets you use any \Closure object to define a
function or method. Thus:function reduce = $initial ~> $fn ~> $input ~> { // ... };
I like this but we're getting a bit too far in the future RFC
territory. But bringing this back to the current proposal I think that
the curly brace syntax should stay. I much prefer this:
function reduce = $initial ~> $fn ~> $input ~> {
$accumulator = $initial;
foreach ($input as $value) {
$accumulator = $fn($accumulator, $value);
}
return $accumulator;
};
To this:
function reduce = $initial ~> $fn ~> function($input) use($initial, $fn) {
$accumulator = $initial;
foreach ($input as $value) {
$accumulator = $fn($accumulator, $value);
}
return $accumulator;
};
So while assigning the result to a named function (instead of a
variable) is out of scope of this RFC I think it demonstrates the
value of the block syntax which I think we should keep. I've said this
several times now, so I won't say it again :)
This has that advantage that it isn't just useful for when you want to use
the short syntax. You can also use it when you're obtaining a Closure in
some novel way, perhaps a higher-order function:// where "add" is a function that adds two numbers function sum = reduce(0)("add");
With this, you don't have to write a wrapper function, or use a global
variable, to define a proper top-level function.
I like this as well but again we're getting a bit too far away from
the current proposal.
Pavel Kouřil wrote on 03/09/2015 07:32:
I wouldn't mind the
mandatory ( ) around parameters, but at the same time I think that
it's an useless rule and makes the written code more "cluttered" (too
much ( and ) imho make the code less readable).
Bear in mind that under the current proposal, the parentheses are only
optional in the special case of a closure with exactly one parameter.
Any time you want zero parameters, or more than one, you have to put
them in anyway.
Amendment 1. Only allow the single-expression form of the short syntax; full
function bodies must be declared with the existing function(){ ... } syntax.
This gives a clearer distinction between how the two syntaxes can be used.
I'm down with that. Short syntax works best when it's concise. If my
callback needs to do more, I tend to prefer writing the method
elsewhere, and naming the variable its in according to what it does.
Yay readability!
Amendment 2. Make the ~> operator non-associative, so that lambdas returning
lambdas would need an extra pair of brackets making the nesting explicit. To
take one of Anthony's examples:function partial(callable $cb) {
return $left ~> ( $right ~> $cb($left, $right) );
}It's now at least clearer that there are two different anonymous functions
here, even if it's not entirely clear what they're trying to achieve...
Again, I support this. I use parenthesis generously so that there's
never a doubt. And because we got ternary's associativity wrong.
Amendment 3. Make the parentheses around the argument list mandatory so that
(if Amendment 1 is also adopted) there is only a single variant of the
syntax, namely "( param list ) ~> expression".
I like it for consistency, but I will say that 80% of my short lambda
uses are callbacks for map and filter, which are single arg callbacks
(when used without keys), so I do seldom see parens used at all.... in
fact, I think that just convinced me why we need to be consistent.
Imagine someone not understanding why adding a second parameter breaks
their short lamba declaration...
Amendment 4. Consider a syntax involving an explicit "use". This one
requires a bit more exploration, but maybe something like:
( $a use $b ) ~> $a + $b
or:
( $a ) use ( $b ) ~> $a + $b
Not a fan of this one at all. Maybe it's my Javascript showing
through, but I like the automatic capture. Not that I'd cry foul if
it were required though. Good short lambdas are short short lambdas,
so their use statements would be small anyway.
-Sara
Den 2015-09-03 kl. 00:48, skrev Rowan Collins:
Beautiful code is code that is easier to
maintain, and code that is easier to maintain is often more secure and
reliable.I think this is an important point. Obviously, "beauty" is subjective:
some people will find very verbose, English-like code easier to read;
others might try to squeeze as much information into one screenful as
possible, so they can see the whole program at a glance.At one extreme, SQL has mandatory words like the "AS" in "CAST(x AS
int)" just to "look more like English"; at the other, APL has symbols
all of its own with their own Unicode code points. Most languages aim
for some compromise in between.So I would like to put forward for consideration these amendments to
the proposal, in the spirit of compromise (in no particular order;
numbers are just for reference in future discussion):Amendment 1. Only allow the single-expression form of the short
syntax; full function bodies must be declared with the existing
function(){ ... } syntax. This gives a clearer distinction between how
the two syntaxes can be used.Amendment 2. Make the ~> operator non-associative, so that lambdas
returning lambdas would need an extra pair of brackets making the
nesting explicit. To take one of Anthony's examples:function partial(callable $cb) {
return $left ~> ( $right ~> $cb($left, $right) );
}It's now at least clearer that there are two different anonymous
functions here, even if it's not entirely clear what they're trying to
achieve...Amendment 3. Make the parentheses around the argument list mandatory
so that (if Amendment 1 is also adopted) there is only a single
variant of the syntax, namely "( param list ) ~> expression".Amendment 4. Consider a syntax involving an explicit "use". This one
requires a bit more exploration, but maybe something like:
( $a use $b ) ~> $a + $b
or:
( $a ) use ( $b ) ~> $a + $bPerhaps there are other thoughts along these lines that could move the
proposal beyond a simple love/hate debate.Regards,
Any plans to incorporate any of the amendments above in the RFC?
Personally I favour amendment 3. One of your examples would be:
functionreduce(callable $fn){
return($initial)~>($input)~>{
$accumulator=$initial;
foreach($inputas$value){
$accumulator=$fn($accumulator,$value);
}
return$accumulator;};
}
Or maybe I'm just overly familiar having function arguments
within parenthesis ;)
Regards, //Björn Larsson
Am 10.09.2015 um 22:04 schrieb Björn Larsson bjorn.x.larsson@telia.com:
Den 2015-09-03 kl. 00:48, skrev Rowan Collins:
Amendment 3. Make the parentheses around the argument list mandatory so that (if Amendment 1 is also adopted) there is only a single variant of the syntax, namely "( param list ) ~> expression".
Any plans to incorporate any of the amendments above in the RFC?
Personally I favour amendment 3. One of your examples would be:
functionreduce(callable $fn){
return($initial)~>($input)~>{
$accumulator=$initial;
foreach($inputas$value){
$accumulator=$fn($accumulator,$value);
}
return$accumulator;};
}Or maybe I'm just overly familiar having function arguments
within parenthesis ;)Regards, //Björn Larsson
You are probably just very familiar with them ;-)
It totally isn't unusual to have that (for languages allowing untyped params), see:
Javascript (ES6)
Hack
C#
(There probably are more, I just can recall these three off my head)
So, I think this very much falls under personal preference.
Bob
Den 2015-09-10 kl. 22:22, skrev Bob Weinand:
Am 10.09.2015 um 22:04 schrieb Björn Larsson bjorn.x.larsson@telia.com:
Den 2015-09-03 kl. 00:48, skrev Rowan Collins:
Amendment 3. Make the parentheses around the argument list mandatory so that (if Amendment 1 is also adopted) there is only a single variant of the syntax, namely "( param list ) ~> expression".
Any plans to incorporate any of the amendments above in the RFC?
Personally I favour amendment 3. One of your examples would be:
functionreduce(callable $fn){
return($initial)~>($input)~>{
$accumulator=$initial;
foreach($inputas$value){
$accumulator=$fn($accumulator,$value);
}
return$accumulator;};
}Or maybe I'm just overly familiar having function arguments
within parenthesis ;)Regards, //Björn Larsson
You are probably just very familiar with them ;-)It totally isn't unusual to have that (for languages allowing untyped params), see:
Javascript (ES6)
Hack
C#(There probably are more, I just can recall these three off my head)
So, I think this very much falls under personal preference.
Bob
Yes, for me it's about not needing to think whether parenthesis
should be there or not. So one more thing to not think about...
Anyway, I noticed that both C# and Hack uses lamda functions for
asynchronous programming, so good to get going with this RFC!
Hack example:
|async (int $x, string $y): Awaitable<Foo> ==> await $x->genFoo($y, $z);
//Björn
|
I had this RFC in draft since some time, but delayed it due to all the
ongoing PHP 7 discussions. Also we have no master branch to merge features
in until 5.4 EOL. Thus I'm reviving this now.Time for the first RFC targeting PHP 7.1 (assuming PHP 8 isn't going to be
the next version ;-)):The short Closures RFC:
https://wiki.php.net/rfc/short_closuresHoping for constructive feedback,
Bob
As a PHP developer (not an internals developer) - I love this proposal!
Out of interest, is there a technical reason you couldn't use an existing
token, such as => for this? If you could, it'd be almost identical to the
syntax used in C#:
ThingList.Where(x => x > 7)
PersonList.Where(p => (p.Gender == "M" && p.Age < 30))
Dan
Dan Cryer wrote on 03/09/2015 11:40:
Out of interest, is there a technical reason you couldn't use an existing
token, such as => for this?
https://wiki.php.net/rfc/short_closures#symbol_choice
In particular, note that this is already valid code:
[$x => $x * 2]
(A one-element array, specifying the key)
But with the proposal, this would also be perfectly reasonable:
[$x ~> $x * 2]
(A one-element array containing a closure)
So clearly they couldn't share the same syntax.
Regards,
Rowan Collins
[IMSoP]
Hi Bob
-----Ursprüngliche Nachricht-----
Von: Bob Weinand [mailto:bobwei9@hotmail.com]
Gesendet: Montag, 31. August 2015 21:29
An: PHP Internals
Betreff: [PHP-DEV] [RFC] [Discussion] Short ClosuresI had this RFC in draft since some time, but delayed it due to all the ongoing PHP 7 discussions. Also we have no
master
branch to merge features in until 5.4 EOL. Thus I'm reviving this now.Time for the first RFC targeting PHP 7.1 (assuming PHP 8 isn't going to be the next version ;-)):
The short Closures RFC:
https://wiki.php.net/rfc/short_closuresHoping for constructive feedback,
Bob
I would like to see a short syntax for closures in PHP but would suggest to use different symbols for the operator. Why
not use --> ?
Hi Bob
-----Ursprüngliche Nachricht-----
Von: Bob Weinand [mailto:bobwei9@hotmail.com]
Gesendet: Montag, 31. August 2015 21:29
An: PHP Internals
Betreff: [PHP-DEV] [RFC] [Discussion] Short ClosuresI had this RFC in draft since some time, but delayed it due to all the
ongoing PHP 7 discussions. Also we have no
master
branch to merge features in until 5.4 EOL. Thus I'm reviving this now.Time for the first RFC targeting PHP 7.1 (assuming PHP 8 isn't going to
be the next version ;-)):The short Closures RFC:
https://wiki.php.net/rfc/short_closuresHoping for constructive feedback,
BobTo unsubscribe,
visit: http://www.php.net/unsub.phpI would like to see a short syntax for closures in PHP but would suggest
to use different symbols for the operator. Why
not use --> ?
--> is a shift-reduce conflict. It's undecidable if this expression should
return boolean or a closure:
$foo-->$bar
(is it $foo --> $bar
or $foo-- > $bar
)
I would like to see a short syntax for closures in PHP but would
suggest to use different symbols for the operator. Why not use --> ?--> is a shift-reduce conflict. It's undecidable if this expression
--> should
return boolean or a closure:
$foo-->$bar
(is it$foo --> $bar
or$foo-- > $bar
)--
You're right, I forgot about the post decrement operator. What about -=> ? would look like a little rocket ^^ but well, it does not really look better than ~
I like many the developers of PHP, write JS lot code, very beg you, if
you can do syntax in ECMAScript 6 (=>)
Thank.