Hey internals,
A few days ago I opened a pull request that adds support for multi-line
arrow functions in PHP: https://github.com/php/php-src/pull/6246.
As you may already know, PHP 7.4 has introduced one-liner arrow functions
(aka short closures). Now, this pull request adds the possibility of those
arrow functions to be multi-line. Just like in Javascript, or Hack. Let's
see an example:
$users = [/** */];
$guestsIds = [/** */];
$repository = /** */;
$guests = array_filter($users, fn ($user) => {
$guest = $repository->findByUserId($user->id);
return $guest !== null && in_array($guest->id, $guestsIds);
});
In short, the advantages are:
$values = array_filter($values, function ($value) use ($first, $second,
$third) {
// vs
$values = array_filter($values, fn ($value) => {
-
Multi-line arrow functions don't require the
use
keyword to be able to
access data from the outer scope. -
Also, in some cases,
fn (/** */) => {
is just shorter and simpler than
function (/** */) use (/** */) {
.
This pull request has been very well received by the community on Github,
and also on Twitter:
https://twitter.com/enunomaduro/status/1311572174256340992.
So, I am writing this email to get your feedback about having this feature
in the core of PHP. Note that, if eventually, you think it is a good idea
to create an RFC on this, I will need to get some RFC karma - my wiki
account is nunomaduro.
Regards,
Nuno - https://twitter.com/enunomaduro.
Hey Nuno,
http://ocramius.github.com/
Hey internals,
A few days ago I opened a pull request that adds support for multi-line
arrow functions in PHP: https://github.com/php/php-src/pull/6246.As you may already know, PHP 7.4 has introduced one-liner arrow functions
(aka short closures). Now, this pull request adds the possibility of those
arrow functions to be multi-line. Just like in Javascript, or Hack. Let's
see an example:$users = [/** */]; $guestsIds = [/** */]; $repository = /** */; $guests = array_filter($users, fn ($user) => { $guest = $repository->findByUserId($user->id); return $guest !== null && in_array($guest->id, $guestsIds); });
In short, the advantages are:
$values = array_filter($values, function ($value) use ($first, $second, $third) { // vs $values = array_filter($values, fn ($value) => {
Multi-line arrow functions don't require the
use
keyword to be able to
access data from the outer scope.Also, in some cases,
fn (/** */) => {
is just shorter and simpler than
function (/** */) use (/** */) {
.This pull request has been very well received by the community on Github,
and also on Twitter:
https://twitter.com/enunomaduro/status/1311572174256340992.So, I am writing this email to get your feedback about having this feature
in the core of PHP. Note that, if eventually, you think it is a good idea
to create an RFC on this, I will need to get some RFC karma - my wiki
account is nunomaduro.
Overall, seems like unnecessary added syntax for something that already
works just fine.
Pros:
- unclear
Cons:
- more scope leak
- +1 way to do what we already do just fine
- more lexer/ast complexity for downstream projects
If you want to make an RFC about it, it really needs a compelling "pros"
section, because there aren't any, so far :-\
Greets,
Marco Pivetta
If you want to make an RFC about it, it really needs a compelling "pros"
section, because there aren't any, so far :-\
I don't agree. There is one pro, and not a small one IMO if you use
closures a lot: skip use().
Since arrow functions have been introduced, I've several times naturally
tried to open braces, only to remember that it's not supported.
I love fn() for the fact that it doesn't require the use() statement, and
would love to see it extended to more than a simple expression.
— Benjamin
Hi Nuno, thank you very much for kicking this off! The current state of
short closure is very limited and I believe it was a stepping stone to get
to this one.
This time there's no drawback of making fn a reserved keyword since it
already is.
This change will lead to better consistency and it will fill in the
expectation gap when using short closure in PHP.
Best of luck for us on this RFC.
Hey internals,
A few days ago I opened a pull request that adds support for multi-line
arrow functions in PHP: https://github.com/php/php-src/pull/6246.As you may already know, PHP 7.4 has introduced one-liner arrow functions
(aka short closures). Now, this pull request adds the possibility of those
arrow functions to be multi-line. Just like in Javascript, or Hack. Let's
see an example:$users = [/** */]; $guestsIds = [/** */]; $repository = /** */; $guests = array_filter($users, fn ($user) => { $guest = $repository->findByUserId($user->id); return $guest !== null && in_array($guest->id, $guestsIds); });
In short, the advantages are:
$values = array_filter($values, function ($value) use ($first, $second, $third) { // vs $values = array_filter($values, fn ($value) => {
Multi-line arrow functions don't require the
use
keyword to be able to
access data from the outer scope.Also, in some cases,
fn (/** */) => {
is just shorter and simpler than
function (/** */) use (/** */) {
.This pull request has been very well received by the community on Github,
and also on Twitter:
https://twitter.com/enunomaduro/status/1311572174256340992.So, I am writing this email to get your feedback about having this feature
in the core of PHP. Note that, if eventually, you think it is a good idea
to create an RFC on this, I will need to get some RFC karma - my wiki
account is nunomaduro.Regards,
Nuno - https://twitter.com/enunomaduro.
Hi Nuno,
A few days ago I opened a pull request that adds support for multi-line
arrow functions in PHP: https://github.com/php/php-src/pull/6246.
Welcome to the list. Firstly, it's probably worth having a look in the
mailing list archives for prior discussions on this, as it was
definitely discussed during the earlier short closures RFCs (the
successful one that gave us fn()=>expr, and a couple of earlier attempts
with different syntax and features).
Secondly, I'd like to point out that "short closures" actually have
three fundamental features:
- They have an implicit "return", making them ideal for single
expressions rather than blocks of procedural code. - They automatically capture all variables in scope, rather than having
to import them with "use". - They are shorter than normal closure declarations, both because of
the above two features, and because "fn" is slightly shorter than
"function".
I think it's worth making clear which of those three features we are
hoping to retain by combining arrow functions with blocks.
Feature 1 doesn't extend to code blocks in an obvious way. In some
languages, every statement is a valid expression, so you can place an
implicit "return" before the last statement of a block. In PHP, that's
not the case, so e.g. { $x = 'Hello World; echo $x; } cannot be
converted to { $x = 'Hello World; return echo $x; }. Alternatives
include converting to { $x = 'Hello World; echo $x; return null; } or
requiring all closure blocks to end with a valid expression.
Feature 2 is probably the one most people actually want extended, but in
my opinion is also the part most in need of justification, because it
changes the language quite fundamentally.
There are currently very few places in PHP where the scope of a variable
is not to the current function: properties of the current object must be
accessed via $this, and class properties via self:: or similar; globals
must be imported via a "global" statement, statics via a "static"
statement, and closed-over values via the "use" keyword. (The main
exception to this rule is the half-dozen built-in "superglobals"; I
think there are a few more obscure cases.)
In a single-expression closure, as currently allowed, there is limited
possibility for ambiguous scope. Extending this to function bodies of
any size leads to much more risk of complexity and confusion.
If you want to capture variables $a, $b, and $c, but have local
variables $x, $y, and $z, you would currently write this:
$f = function() use ($a, $b, $c) {
// $x, $y, $z must be local, because not imported
}
If we added an opt-in syntax for "capture everything", we might instead
write this:
$f = function() use (*) {
$x = $y = $z = null;
}
Without re-initialising all local variables, we would no longer be able
to know if they were actually local without looking at the surrounding
scope for a value that might be captured. I am unconvinced by this
trade-off of opt-out instead of opt-in.
One use case I've seen proposed is closures which capture a large number
of variables; I would be interested to see an example where this is the
case and is not a "code smell" in the same way as requiring a large
number of parameters.
In the above example I deliberately did not use the "fn()=>" syntax,
because I believe this is really orthogonal to the other features - my
impression is that actual expression length (feature 3 above) is more of
a pleasant side-effect than a top priority for most people.
I would personally prefer the "fn()=>" syntax to carry on meaning "this
is an expression elevated to function status", and have some other
syntax for a full closure that uses auto-capturing scope rules if that
feature is indeed needed.
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
Hi Nuno,
A few days ago I opened a pull request that adds support for multi-line
arrow functions in PHP: https://github.com/php/php-src/pull/6246.Welcome to the list. Firstly, it's probably worth having a look in the mailing list archives for prior discussions on this, as it was definitely discussed during the earlier short closures RFCs (the successful one that gave us fn()=>expr, and a couple of earlier attempts with different syntax and features).
Secondly, I'd like to point out that "short closures" actually have three fundamental features:
- They have an implicit "return", making them ideal for single expressions rather than blocks of procedural code.
- They automatically capture all variables in scope, rather than having to import them with "use".
- They are shorter than normal closure declarations, both because of the above two features, and because "fn" is slightly shorter than "function".
I think it's worth making clear which of those three features we are hoping to retain by combining arrow functions with blocks.
Feature 1 doesn't extend to code blocks in an obvious way. In some languages, every statement is a valid expression, so you can place an implicit "return" before the last statement of a block. In PHP, that's not the case, so e.g. { $x = 'Hello World; echo $x; } cannot be converted to { $x = 'Hello World; return echo $x; }. Alternatives include converting to { $x = 'Hello World; echo $x; return null; } or requiring all closure blocks to end with a valid expression.
Feature 2 is probably the one most people actually want extended, but in my opinion is also the part most in need of justification, because it changes the language quite fundamentally.
There are currently very few places in PHP where the scope of a variable is not to the current function: properties of the current object must be accessed via $this, and class properties via self:: or similar; globals must be imported via a "global" statement, statics via a "static" statement, and closed-over values via the "use" keyword. (The main exception to this rule is the half-dozen built-in "superglobals"; I think there are a few more obscure cases.)
In a single-expression closure, as currently allowed, there is limited possibility for ambiguous scope. Extending this to function bodies of any size leads to much more risk of complexity and confusion.
If you want to capture variables $a, $b, and $c, but have local variables $x, $y, and $z, you would currently write this:
$f = function() use ($a, $b, $c) {
// $x, $y, $z must be local, because not imported
}If we added an opt-in syntax for "capture everything", we might instead write this:
$f = function() use (*) {
$x = $y = $z = null;
}Without re-initialising all local variables, we would no longer be able to know if they were actually local without looking at the surrounding scope for a value that might be captured. I am unconvinced by this trade-off of opt-out instead of opt-in.
One use case I've seen proposed is closures which capture a large number of variables; I would be interested to see an example where this is the case and is not a "code smell" in the same way as requiring a large number of parameters.
In the above example I deliberately did not use the "fn()=>" syntax, because I believe this is really orthogonal to the other features - my impression is that actual expression length (feature 3 above) is more of a pleasant side-effect than a top priority for most people.
I would personally prefer the "fn()=>" syntax to carry on meaning "this is an expression elevated to function status", and have some other syntax for a full closure that uses auto-capturing scope rules if that feature is indeed needed.
Good analysis.
I agree #2 is the strongest motivator for some kind of improvement / change.
In at least one prior language I have used there was no distinction between local scope inside and outside of a closure, so when I first realized I had to use the "use" clause to make variable visible it was surprising to me.
In PHP I find this requirement rather annoying in the majority of cases. My functions and closures tend to be short and the number of local variables I use — both inside and outside the closure —tend to be small so I don't have a problem with confusing scope. Having to explicitly name the variables in a use statement feels like unfortunate overkill. But I do get your point.
OTOH, when inherited local variables are NOT modified within the closure, does that actually cause a problem? (honest question)
If not — and please check my logic — I would suggest PHP 8.1+ could allow closures to IMPLICITLY inherit local variables, but ONLY for variables that are READ, not for variables that are written.
Given this approach, variables would still need to be declared when variables need to be written, e.g. "use ( &$foo )"
Although this relaxing of requirement to declare variables could theoretically break existing code, only code where variables are read inside closures before they are assigned with the same name as variables assigned outside the closure would "break", and those cases are flagged as a warning when error reporting is on.
For me, that would probably cover 90%+ of the use cases my the requirement to employ the "use" statement feels like overkill.
-Mike
If we added an opt-in syntax for "capture everything", we might
instead write this:$f = function() use (*) {
$x = $y = $z = null;
}Without re-initialising all local variables, we would no longer be
able to know if they were actually local without looking at the
surrounding scope for a value that might be captured. I am unconvinced
by this trade-off of opt-out instead of opt-in.One use case I've seen proposed is closures which capture a large
number of variables; I would be interested to see an example where
this is the case and is not a "code smell" in the same way as
requiring a large number of parameters.
Something like "use (*)" seems like a great enhancement to me. I often
use a wrapper function for SQL transactions, something like:
public function update(int $numberId, int $addressId, bool $isMainNumber
= false): void
{
$this->transaction->run(function () use ($numberId, $addressId,
$isMainNumber): void {
// Do all SQL queries for the update
});
}
In these cases there is a lot of redundancy because of having to import
the variables, and if a variable is added, it has to be added in two
places in a slightly different way. The following would be much nicer:
public function update(int $numberId, int $addressId, bool $isMainNumber
= false): void
{
$this->transaction->run(function () use (*): void {
// Do all SQL queries for the update
});
}
This would also increase code readability.
Yes, "use (*)" is perfect!
With kind regards / Mit freundlichen Grüßen / S přátelským pozdravem,
Michael Voříšek
On 04.10.20 22:08, Rowan Tommins wrote:
If we added an opt-in syntax for "capture everything", we might
instead write this:$f = function() use (*) {
$x = $y = $z = null;
}Without re-initialising all local variables, we would no longer be
able to know if they were actually local without looking at the
surrounding scope for a value that might be captured. I am unconvinced
by this trade-off of opt-out instead of opt-in.One use case I've seen proposed is closures which capture a large
number of variables; I would be interested to see an example where
this is the case and is not a "code smell" in the same way as
requiring a large number of parameters.Something like "use (*)" seems like a great enhancement to me. I often
use a wrapper function for SQL transactions, something like:public function update(int $numberId, int $addressId, bool $isMainNumber = false): void { $this->transaction->run(function () use ($numberId, $addressId, $isMainNumber): void { // Do all SQL queries for the update }); }
In these cases there is a lot of redundancy because of having to import
the variables, and if a variable is added, it has to be added in two
places in a slightly different way. The following would be much nicer:public function update(int $numberId, int $addressId, bool $isMainNumber = false): void { $this->transaction->run(function () use (*): void { // Do all SQL queries for the update }); }
This would also increase code readability.
--
To unsubscribe, visit: https://www.php.net/unsub.php
On Mon, Oct 5, 2020 at 12:00 PM Michael Voříšek - ČVUT FEL <
vorismi3@fel.cvut.cz> wrote:
Yes, "use (*)" is perfect!
With kind regards / Mit freundlichen Grüßen / S přátelským pozdravem,
Michael Voříšek
If we added an opt-in syntax for "capture everything", we might
instead write this:$f = function() use (*) {
$x = $y = $z = null;
}Without re-initialising all local variables, we would no longer be
able to know if they were actually local without looking at the
surrounding scope for a value that might be captured. I am unconvinced
by this trade-off of opt-out instead of opt-in.One use case I've seen proposed is closures which capture a large
number of variables; I would be interested to see an example where
this is the case and is not a "code smell" in the same way as
requiring a large number of parameters.Something like "use (*)" seems like a great enhancement to me. I often
use a wrapper function for SQL transactions, something like:public function update(int $numberId, int $addressId, bool $isMainNumber = false): void { $this->transaction->run(function () use ($numberId, $addressId, $isMainNumber): void { // Do all SQL queries for the update }); }
In these cases there is a lot of redundancy because of having to import
the variables, and if a variable is added, it has to be added in two
places in a slightly different way. The following would be much nicer:public function update(int $numberId, int $addressId, bool $isMainNumber = false): void { $this->transaction->run(function () use (*): void { // Do all SQL queries for the update }); }
This would also increase code readability.
--
To unsubscribe, visit: https://www.php.net/unsub.php
How should php deal with the scenario where you want to use
everything
and have one variable by reference?
function () use (*, &$butNotThisOne) {};
How should php deal with the scenario where you want to
use
everything
and have one variable by reference?function () use (*, &$butNotThisOne) {};
The easiest would be to only allow "use ()" with no references or
additional syntax. "use ()" would only copy all local variables into
the closure, no references. Personally I have never used references with
"use", I think it is much more niche compared to the regular copying,
and there is still the explicit (current) syntax to do references.
How should php deal with the scenario where you want to
use
everything
and have one variable by reference?function () use (*, &$butNotThisOne) {};
The easiest would be to only allow "use ()" with no references or
additional syntax. "use ()" would only copy all local variables into
the closure, no references. Personally I have never used references with
"use", I think it is much more niche compared to the regular copying,
and there is still the explicit (current) syntax to do references.
FTR, short arrow function implicitly only support "by value" bindings, I
just checked the docs.
I do use by ref with closures btw.
- Markus
How should php deal with the scenario where you want to
use
everything
and have one variable by reference?function () use (*, &$butNotThisOne) {};
I would take a page out of C++'s book here. In C++ a closure is (some of
these bits can be omitted for brevity, but I'm not going to describe those
here as it's orthogonal to the topic):
[capture, vars](type argName) -> returnType {
statements;
return retVal;
}
Looking specifically at the capture vars section (in the example we are
capturing two variables, one named 'capture', one named 'vars'), there are
wildcards available as well:
[=](bool bval) -> void {}
The equal sign (as above) captures all variables used in the closure by
value.
Similarly, the ampersand [&](int ival) -> void {}
captures all variables
used by reference.
Exceptions can be added to that list just as you suggested, so:
[=,&foo](double dval) -> double { return foo + dval; }
Or
&,foo -> void { doSomething(foo); }
I think we could apply the same in PHP terms:
function(float $dval) use ($, &$foo) { return $foo + $dval; };
function() use (&$, $foo) { doSomething($foo); };
Plus or minor for parser convenience.
-Sara
To me that seems like a great argument in favour of the proposal. If you'll
want all variables to be imported (which in this case makes completely
sense), then fn() {}
or fn() => {}
is much less verbose and inline with
the mentality to reach for short closures. We reach for short closures to
avoid use()
and convey that the outer process is intertwined with the
inner process. fn()
allows to strengthen the concept that there's no real
separation between running SQL stuff in a callable
that wraps a database
transaction.
If we added an opt-in syntax for "capture everything", we might
instead write this:$f = function() use (*) {
$x = $y = $z = null;
}Without re-initialising all local variables, we would no longer be
able to know if they were actually local without looking at the
surrounding scope for a value that might be captured. I am unconvinced
by this trade-off of opt-out instead of opt-in.One use case I've seen proposed is closures which capture a large
number of variables; I would be interested to see an example where
this is the case and is not a "code smell" in the same way as
requiring a large number of parameters.Something like "use (*)" seems like a great enhancement to me. I often
use a wrapper function for SQL transactions, something like:public function update(int $numberId, int $addressId, bool $isMainNumber = false): void { $this->transaction->run(function () use ($numberId, $addressId, $isMainNumber): void { // Do all SQL queries for the update }); }
In these cases there is a lot of redundancy because of having to import
the variables, and if a variable is added, it has to be added in two
places in a slightly different way. The following would be much nicer:public function update(int $numberId, int $addressId, bool $isMainNumber = false): void { $this->transaction->run(function () use (*): void { // Do all SQL queries for the update }); }
This would also increase code readability.
--
To unsubscribe, visit: https://www.php.net/unsub.php
--
Marco Aurélio Deleu
To me that seems like a great argument in favour of the proposal. If you'll
want all variables to be imported (which in this case makes completely
sense), thenfn() {}
orfn() => {}
is much less verbose and inline with
the mentality to reach for short closures. We reach for short closures to
avoiduse()
and convey that the outer process is intertwined with the
inner process.fn()
allows to strengthen the concept that there's no real
separation between running SQL stuff in acallable
that wraps a database
transaction.
Not necessarily: the arrow functions were specifically implemented for
very short anonymous functions with a return value. Making them more and
more like the existing "function" syntax would lead to having two
different ways of defining anonymous functions that mainly differentiate
themselves by including the parent scope by default or not.
I like the "function () use (*)" suggestion because it is explicit and
opt-in. A shorter syntax like "fn () {}" is less clear, and it could
lead to many people always using fn just because it is faster to write
(and less to think about), which then could lead to unintended side
effects because variables are being copied from the parent scope each
time. When you see a usage of "fn () {}" while reading code you would
not know if the person used it because it was faster to write, or if the
parent scope variables are really needed.
Hi internals
The reason multi-line short closures are so valued by userland devs is because they are shorter to write and prettier to read. While some of us might not agree on the definition of "prettier to read", it was one of the key arguments for adding short closures in the first place:
Anonymous functions in PHP can be quite verbose, even when they only perform a simple operation. Partly this is due to a large amount of syntactic boilerplate, and partly due to the need to manually import used variables. This makes code using simple closures hard to read and understand. This RFC proposes a more concise syntax for this pattern. [1]
We can have the discussion again on whether we like short closures or not, but it turned out most of internals and userland devs do — based on the vote count in the sigle line RFC and the reaction on Nuno's PR, as well as my experience from an OSS maintainer point of view.
Furthermore, the use(*)
syntax misses the point of this proposal: it's not about being able to use all variables from the outer scope, it's about a clean syntax that's as short as possible — even when you personally disagree that it is. I've made the same argument before on this list: it's clear that the PHP community wants these changes: named arguments, property promotions, short closures, … these are all features that aren't necessary, still they are great features of a modern-day language.
I also want to quote from Larry Garfields book on thinking functionally in PHP [2], to demonstrate the signicant impact short closures already had today:
“Combined with PHP’s overall clunky syntax for doing functional-esque code, I generally didn’t go further than “pure functions are your friend,” either in my own code or what I explained to others.
That is, until PHP 7.4.
PHP 7.4’s introduction of short lambdas is, as we’ll see in this book, a game-changer. While it doesn’t make anything new possible, it makes a lot of things suddenly practical. That makes all the difference, so I decided it was time to buckle down and really dig into functional programming.”
Larry continues to write a whole book about functional programming in PHP, and short closures play a significant role.
So I hope to see more input on Nuno's PR from a techinical point of view: what's missing, what's needed to get this to the RFC phase, … and not only discussions about what syntax we like or not, or whether there are other ways to solve the same problem. Please provide Nuno with actionable feedback.
Kind regards
Brent
[1] https://wiki.php.net/rfc/arrow_functions_v2 https://wiki.php.net/rfc/arrow_functions_v2
[2] https://leanpub.com/thinking-functionally-in-php https://leanpub.com/thinking-functionally-in-php
To me that seems like a great argument in favour of the proposal. If you'll
want all variables to be imported (which in this case makes completely
sense), thenfn() {}
orfn() => {}
is much less verbose and inline with
the mentality to reach for short closures. We reach for short closures to
avoiduse()
and convey that the outer process is intertwined with the
inner process.fn()
allows to strengthen the concept that there's no real
separation between running SQL stuff in acallable
that wraps a database
transaction.Not necessarily: the arrow functions were specifically implemented for
very short anonymous functions with a return value. Making them more and
more like the existing "function" syntax would lead to having two
different ways of defining anonymous functions that mainly differentiate
themselves by including the parent scope by default or not.I like the "function () use (*)" suggestion because it is explicit and
opt-in. A shorter syntax like "fn () {}" is less clear, and it could
lead to many people always using fn just because it is faster to write
(and less to think about), which then could lead to unintended side
effects because variables are being copied from the parent scope each
time. When you see a usage of "fn () {}" while reading code you would
not know if the person used it because it was faster to write, or if the
parent scope variables are really needed.--
To unsubscribe, visit: https://www.php.net/unsub.php
First, can you please bottom-post and not top-post.
Hi internals
The reason multi-line short closures are so valued by userland devs is
because they are shorter to write and prettier to read. While some of us
might not agree on the definition of "prettier to read", it was one of the
key arguments for adding short closures in the first place:Anonymous functions in PHP can be quite verbose, even when they only
perform a simple operation. Partly this is due to a large amount of
syntactic boilerplate, and partly due to the need to manually import used
variables. This makes code using simple closures hard to read and
understand. This RFC proposes a more concise syntax for this pattern. [1]We can have the discussion again on whether we like short closures or not,
but it turned out most of internals and userland devs do — based on the
vote count in the sigle line RFC and the reaction on Nuno's PR, as well as
my experience from an OSS maintainer point of view.
I didn't know we were meant to do code golfing with production code, might
have missed a memo somewhere.
Furthermore, the
use(*)
syntax misses the point of this proposal: it's
not about being able to use all variables from the outer scope,
If it's not about being able to use all variables (or even just one that is
irrelevant) from the outer scope, then what is the point?
Saving 6 characters by only writing fn() {} instead of function {}?
it's about a clean syntax that's as short as possible — even when you
personally disagree that it is. I've made the same argument before on this
list: it's clear that the PHP community wants these changes:
Wanting something isn't an argument. Looking at what part of the community
wants, we should be using @ for attributes.
named arguments, property promotions, short closures, … these are all
features that aren't necessary, still they are great features of a
modern-day language.
Obviously nothing is necessary, we could write assembler style with only
goto statements.
I also want to quote from Larry Garfields book on thinking functionally in
PHP [2], to demonstrate the signicant impact short closures already had
today:“Combined with PHP’s overall clunky syntax for doing functional-esque
code, I generally didn’t go further than “pure functions are your friend,”
either in my own code or what I explained to others.That is, until PHP 7.4.
PHP 7.4’s introduction of short lambdas is, as we’ll see in this book, a
game-changer. While it doesn’t make anything new possible, it makes a lot
of things suddenly practical. That makes all the difference, so I decided
it was time to buckle down and really dig into functional programming.”Larry continues to write a whole book about functional programming in PHP,
and short closures play a significant role.
Finally a resemblance of an actual argument.
So I hope to see more input on Nuno's PR from a techinical point of view:
what's missing, what's needed to get this to the RFC phase, … and not only
discussions about what syntax we like or not, or whether there are other
ways to solve the same problem. Please provide Nuno with actionable
feedback.Kind regards
Brent[1] https://wiki.php.net/rfc/arrow_functions_v2 <
https://wiki.php.net/rfc/arrow_functions_v2>
[2] https://leanpub.com/thinking-functionally-in-php <
https://leanpub.com/thinking-functionally-in-php>
Jokes aside, the actionable feedback is to argue why auto capture of the
outer scope should be added to the
language as "it is very not PHP" a direct quote from Rasmus from his talk
"25 years of PHP" [1] and from the
same section one of the reasons why people don't mind the current single
line expression form is because it
doesn't look like a new scope.
As Rowan said in his analysis changing this specific behaviour of scope
being able to "leak" into another one
needs a lot of justification, the current short closure syntax doesn't even
use braces {} which are the de facto
signal in PHP that you are entering in a new scope.
Going back to the use(*)
syntax: the reason why people propose this
extension (which is not mutually exclusive
with adding support for fn {} without outer scope capture, albeit strange)
is that it is more in PHP traditional design
philosophy. You can argue against this syntax and in favour of Nuno's, but
again it is NOT missing the point.
Moreover, Larry has also made a PR which extends short closures [2] in a
way I personally find way more appealing.
Regards
George P. Banyard
[1] https://youtu.be/Qa_xVjTiOUw?t=1895
[2] https://github.com/php/php-src/pull/6221
First, can you please bottom-post and not top-post.
Hi internals
The reason multi-line short closures are so valued by userland devs is
because they are shorter to write and prettier to read. While some of us
might not agree on the definition of "prettier to read", it was one of the
key arguments for adding short closures in the first place:Anonymous functions in PHP can be quite verbose, even when they only
perform a simple operation. Partly this is due to a large amount of
syntactic boilerplate, and partly due to the need to manually import used
variables. This makes code using simple closures hard to read and
understand. This RFC proposes a more concise syntax for this pattern. [1]We can have the discussion again on whether we like short closures or
not, but it turned out most of internals and userland devs do — based on
the vote count in the sigle line RFC and the reaction on Nuno's PR, as well
as my experience from an OSS maintainer point of view.I didn't know we were meant to do code golfing with production code, might
have missed a memo somewhere.Furthermore, the
use(*)
syntax misses the point of this proposal: it's
not about being able to use all variables from the outer scope,If it's not about being able to use all variables (or even just one that
is irrelevant) from the outer scope, then what is the point?
Saving 6 characters by only writing fn() {} instead of function {}?it's about a clean syntax that's as short as possible — even when you
personally disagree that it is. I've made the same argument before on this
list: it's clear that the PHP community wants these changes:Wanting something isn't an argument. Looking at what part of the community
wants, we should be using @ for attributes.named arguments, property promotions, short closures, … these are all
features that aren't necessary, still they are great features of a
modern-day language.Obviously nothing is necessary, we could write assembler style with only
goto statements.I also want to quote from Larry Garfields book on thinking functionally in
PHP [2], to demonstrate the signicant impact short closures already had
today:“Combined with PHP’s overall clunky syntax for doing functional-esque
code, I generally didn’t go further than “pure functions are your friend,”
either in my own code or what I explained to others.That is, until PHP 7.4.
PHP 7.4’s introduction of short lambdas is, as we’ll see in this book,
a game-changer. While it doesn’t make anything new possible, it makes a lot
of things suddenly practical. That makes all the difference, so I decided
it was time to buckle down and really dig into functional programming.”Larry continues to write a whole book about functional programming in
PHP, and short closures play a significant role.Finally a resemblance of an actual argument.
So I hope to see more input on Nuno's PR from a techinical point of view:
what's missing, what's needed to get this to the RFC phase, … and not only
discussions about what syntax we like or not, or whether there are other
ways to solve the same problem. Please provide Nuno with actionable
feedback.Kind regards
Brent[1] https://wiki.php.net/rfc/arrow_functions_v2 <
https://wiki.php.net/rfc/arrow_functions_v2>
[2] https://leanpub.com/thinking-functionally-in-php <
https://leanpub.com/thinking-functionally-in-php>Jokes aside, the actionable feedback is to argue why auto capture of the
outer scope should be added to the
language as "it is very not PHP" a direct quote from Rasmus from his talk
"25 years of PHP" [1] and from the
same section one of the reasons why people don't mind the current single
line expression form is because it
doesn't look like a new scope.
As Rowan said in his analysis changing this specific behaviour of scope
being able to "leak" into another one
needs a lot of justification, the current short closure syntax doesn't
even use braces {} which are the de facto
signal in PHP that you are entering in a new scope.Going back to the
use(*)
syntax: the reason why people propose this
extension (which is not mutually exclusive
with adding support for fn {} without outer scope capture, albeit strange)
is that it is more in PHP traditional design
philosophy. You can argue against this syntax and in favour of Nuno's, but
again it is NOT missing the point.Moreover, Larry has also made a PR which extends short closures [2] in a
way I personally find way more appealing.Regards
George P. Banyard
[1] https://youtu.be/Qa_xVjTiOUw?t=1895
[2] https://github.com/php/php-src/pull/6221
Ressending to the list as I seem to have messed up my reply.
Hi Brent,
We can have the discussion again on whether we like short closures or not, but it turned out most of internals_and_ userland devs do — based on the vote count in the sigle line RFC and the reaction on Nuno's PR, as well as my experience from an OSS maintainer point of view.
I don't know how much of the earlier discussion you saw at the time, or
have looked back at, but it's worth noting that the arrow functions RFC
which passed voting came after several long mailing list threads, and
several complete rewrites of the proposal, at least one of which went to
vote and was declined. The lack of multi-statement arrow functions
wasn't an accident, it was part of how consensus was achieved.
That doesn't mean we can't discuss multi-statement arrow functions now -
they were listed as "Future Scope", and we are now in that Future. :)
However, it does mean a new RFC doing so needs to set out its own
justification, not assume that everyone who voted Yes on the previous
RFC will vote Yes on this one as well.
Furthermore, the
use(*)
syntax misses the point of this proposal: it's not about being able to use all variables from the outer scope, it's about a clean syntax that's as short as possible
Rather than "missing the point", I think it's a case of different people
wanting different things from arrow functions. In one of the previous
discussions [1] I suggested three perspectives of what people wanted
them for:
(a) an improved syntax for declaring closures (in the way that [$foo,
$bar] is a shorter syntax for array($foo, $bar))
(b) improved semantics for declaring closures, where you don't have to
list the variables to be captured
(c) a new kind of closure designed for specific use cases, with its own
syntax and semantics
Those who wanted (c) are generally happy with what we have, with perhaps
an interest in making more statements available as expressions, such as
throw [2] and match [3].
Those who wanted (b) generally see the arrow syntax as a means to an
end, and are open to alternative syntaxes that achieve the relevant
semantics, such as use(*).
Those who wanted (a) may see things the other way around: the short
syntax is the goal, and auto-capture and implicit return are just the
ways to achieve that goal.
There's probably no way to please all three groups (and the various
combinations and nuances I've over-simplified), but it's useful to
acknowledge the existence of those who want some of what you want, but
don't agree on all of it.
I look forward to seeing the RFC if Nuno decides to proceed with it,
because I am much more interested in how it will affect users of PHP
than the technical details visible in the PR.
[1] https://externals.io/message/98045#98209
[2] https://wiki.php.net/rfc/throw_expression
[3] https://wiki.php.net/rfc/match_expression_v2
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]