(Email in gist format:
https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c)
In EcmaScript 2015 (ES6) the expression (x) => x * 2
means to create
an anonymous function with one parameter x
that will return x * 2
.
For example:
(x) => x * 2
// is equivalent to:
function(x) { return x * 2; }
A modified example from documentation by Mozilla Developer
Network page demonstrates how they are useful:
var a = [
"Hydrogen",
"Helium",
"Lithium",
"Beryllium"
];
var a2 = a.map(function(s){ return s.length }); // pre-ES6
var a3 = a.map((s) => s.length); // ES6
There has been some talk about how we can use arrow function
expressions in PHP. In PHP using the same syntax would have some
ambiguities:
// Does this mean:
// 1. Create an array key with the result of `($x)` and a value
with $x * 2
// 2. Create an array with one value that is an anonymous function
[($x) => $x * 2]
// Does this mean:
// 1. Yield a key with the result of `($x)` and a value with `$x * 2`
// 2. Yield an anonymous function
yield ($x) => $x * 2;
This is why Bob Weinand proposed using ~>
instead of =>
.
However, if we allow type declarations there is another issue. In the
definition (Type &$x) => expr
the (Type &$var)
part can parse as
"take constant Type
and variable $var
and do a bitwise and &
operation." After that the =>
will be an unexpected token. Even
though the rule would be invalid the parser doesn't know that far
ahead it will error and it doesn't know which rule to pick. Changing
the token from =>
to ~>
doesn't affect this issue.
We could solve the first ambiguities with prefering the current
meaning with key => value
and requiring the meaning with closures to
wrap them in ()
. We could solve the latter ambiguity with a
backtracking parser since it will eventually error and then know to
pick the other rule. However, I really think this is a bad idea.
So how can we have shorter closures without this mess? One simple way
is to require the function
prefix:
// clearly an array with an anonymous function
[function($x) => $x * 2];
// clearly yields an anonymous function
yield function($x) => $x * 2;
// clearly an anonymous function
function(Type &$x) => expr;
Requiring the function
prefix mitigates one of the value parts of
arrow functions: they are short.
Another option would be to resolve the ambiguities with keys and
values but to change the type information in parameters:
(&$input: array) => expr
By putting the type after the variable (similar to how we declare
return types) we no longer have the issues with mis-parsing. Of
course, that's not how we declare parameter types currently. I think
we would need to permit it everywhere and deprecate the current syntax
with the type being prefixed. (By deprecate I mean in PHP 8 and not
remove it until PHP 9 or later)
I would prefer that we shorten the function
keyword to fn
:
[fn($x) => $x * 2]
This preserves the shortness of the expression while providing
unambiguous, simple parsing. Of course, now we have a similar issue:
we have both fn
and function
.
What concerns do you have about fn($x) => $x * 2
or function($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.
(Email in gist format:
https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c)In EcmaScript 2015 (ES6) the expression
(x) => x * 2
means to create
an anonymous function with one parameterx
that will returnx * 2
.
For example:(x) => x * 2 // is equivalent to: function(x) { return x * 2; }
A modified example from documentation by Mozilla Developer
Network page demonstrates how they are useful:var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(function(s){ return s.length }); // pre-ES6 var a3 = a.map((s) => s.length); // ES6
There has been some talk about how we can use arrow function
expressions in PHP. In PHP using the same syntax would have some
ambiguities:// Does this mean: // 1. Create an array key with the result of `($x)` and a value
with
$x * 2
// 2. Create an array with one value that is an anonymous function
[($x) => $x * 2]// Does this mean: // 1. Yield a key with the result of `($x)` and a value with `$x * 2` // 2. Yield an anonymous function yield ($x) => $x * 2;
This is why Bob Weinand proposed using
~>
instead of=>
.
However, if we allow type declarations there is another issue. In the
definition(Type &$x) => expr
the(Type &$var)
part can parse as
"take constantType
and variable$var
and do a bitwise and&
operation." After that the=>
will be an unexpected token. Even
though the rule would be invalid the parser doesn't know that far
ahead it will error and it doesn't know which rule to pick. Changing
the token from=>
to~>
doesn't affect this issue.We could solve the first ambiguities with prefering the current
meaning withkey => value
and requiring the meaning with closures to
wrap them in()
. We could solve the latter ambiguity with a
backtracking parser since it will eventually error and then know to
pick the other rule. However, I really think this is a bad idea.So how can we have shorter closures without this mess? One simple way
is to require thefunction
prefix:// clearly an array with an anonymous function [function($x) => $x * 2]; // clearly yields an anonymous function yield function($x) => $x * 2; // clearly an anonymous function function(Type &$x) => expr;
Requiring the
function
prefix mitigates one of the value parts of
arrow functions: they are short.Another option would be to resolve the ambiguities with keys and
values but to change the type information in parameters:(&$input: array) => expr
By putting the type after the variable (similar to how we declare
return types) we no longer have the issues with mis-parsing. Of
course, that's not how we declare parameter types currently. I think
we would need to permit it everywhere and deprecate the current syntax
with the type being prefixed. (By deprecate I mean in PHP 8 and not
remove it until PHP 9 or later)I would prefer that we shorten the
function
keyword tofn
:[fn($x) => $x * 2]
This preserves the shortness of the expression while providing
unambiguous, simple parsing. Of course, now we have a similar issue:
we have bothfn
andfunction
.What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.
I forgot to mention that the arrow expressions would close over any
values that exist in their parent scope. Like Bob's proposal this
happens by value (in fact the draft I have for it simply replaces the
parser and reuses his implementation):
function add($x) {
return fn($y) => $x + $y;
}
add(1)(2); // int(3)
What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.
If a keyword is required next to the parameters, having the => as a
separate token looks a bit weird. It no longer matches other languages,
so how about thinking a bit further outside the box?
One of the random thoughts that popped into my head during the previous
discussion was to base the syntax on the C for-loop, which would also
give a place for bound variables without the "use" keyword. e.g. your
example could become fn($x;; $x * 2)
I picked "lambda" as the keyword before, and gave these examples:
lambda(params; bound vars; expression)
$double = lambda($a;; 2*$a)
$x=3; $triple = lambda($a; $x; $x * $a)
function sumEventScores($events, $scores) {
$types = array_map(lambda($event;; $event['type']), $events);
return array_reduce($types, lambda($sum, $type; $scores; $sum +
$scores[$type]));
}
Adding in the type information, we'd get this:
lambda(int $sum, string $type; $scores; $sum + $scores[$type])
or with fn() if you prefer:
fn(int $sum, string $type; $scores; $sum + $scores[$type])
If return typehints are also required, I'm not sure where they'd best be
placed. If they're outside the parens, they end up after the expression,
which might look odd:
fn(int $sum, string $type; $scores; $sum + $scores[$type]): int
A few other possibilities:
fn(int $sum, string $type; $scores; $sum + $scores[$type]; int)
fn(int $sum, string $type: int; $scores; $sum + $scores[$type])
fn:int(int $sum, string $type; $scores; $sum + $scores[$type])
All of this assumes that the shorthand is only available for simple
expressions, not function bodies, but it seems a bit rendundant to allow
both of these:
function($x) { foo(); bar(); baz(); }
fn($x) => { foo(); bar(); baz(); }
And only marginally more useful if variables are auto-captured, with all
the downsides of that which have already been raised:
function($x) use ($y) { foo($x); bar($x, $y); }
fn($x) => { foo($x); bar($x, $y); }
Regards,
--
Rowan Collins
[IMSoP]
What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.If a keyword is required next to the parameters, having the => as a separate
token looks a bit weird. It no longer matches other languages, so how about
thinking a bit further outside the box?
Thank you for the feedback. I feel like the rest of what you proposed
was a bit too far outside of the box. For what it is worth no token
after the paren is necessary – you could do fn($x) $x * 2
(or
function($x) $x * 2
). I think this is a case where more syntax
helps.
What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.If a keyword is required next to the parameters, having the => as a separate
token looks a bit weird. It no longer matches other languages, so how about
thinking a bit further outside the box?One of the random thoughts that popped into my head during the previous
discussion was to base the syntax on the C for-loop, which would also give a
place for bound variables without the "use" keyword. e.g. your example could
become fn($x;; $x * 2)I picked "lambda" as the keyword before, and gave these examples:
lambda(params; bound vars; expression)
$double = lambda($a;; 2*$a)
$x=3; $triple = lambda($a; $x; $x * $a)function sumEventScores($events, $scores) {
$types = array_map(lambda($event;; $event['type']), $events);
return array_reduce($types, lambda($sum, $type; $scores; $sum +
$scores[$type]));
}Adding in the type information, we'd get this:
lambda(int $sum, string $type; $scores; $sum + $scores[$type])
or with fn() if you prefer:
fn(int $sum, string $type; $scores; $sum + $scores[$type])
If return typehints are also required, I'm not sure where they'd best be
placed. If they're outside the parens, they end up after the expression,
which might look odd:
fn(int $sum, string $type; $scores; $sum + $scores[$type]): intA few other possibilities:
fn(int $sum, string $type; $scores; $sum + $scores[$type]; int)
fn(int $sum, string $type: int; $scores; $sum + $scores[$type])
fn:int(int $sum, string $type; $scores; $sum + $scores[$type])All of this assumes that the shorthand is only available for simple
expressions, not function bodies, but it seems a bit rendundant to allow
both of these:
function($x) { foo(); bar(); baz(); }
fn($x) => { foo(); bar(); baz(); }And only marginally more useful if variables are auto-captured, with all the
downsides of that which have already been raised:
function($x) use ($y) { foo($x); bar($x, $y); }
fn($x) => { foo($x); bar($x, $y); }Regards,
--
Rowan Collins
[IMSoP]
What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.If a keyword is required next to the parameters, having the => as a separate
token looks a bit weird. It no longer matches other languages, so how about
thinking a bit further outside the box?Thank you for the feedback. I feel like the rest of what you proposed
was a bit too far outside of the box. For what it is worth no token
after the paren is necessary – you could dofn($x) $x * 2
(or
function($x) $x * 2
). I think this is a case where more syntax
helps.
Here they are on separate lines so you can see things more clearly:
$output = array_map(function($x) $x * 2, $input);
$output = array_map(function($x) => $x * 2, $input);
I think the arrow helps.
Thank you for the feedback. I feel like the rest of what you proposed
was a bit too far outside of the box. For what it is worth no token
after the paren is necessary – you could dofn($x) $x * 2
(or
function($x) $x * 2
). I think this is a case where more syntax
helps.
Here they are on separate lines so you can see things more clearly:$output = array_map(function($x) $x * 2, $input); $output = array_map(function($x) => $x * 2, $input);
I think the arrow helps.
I meant more that the lack of parens around the whole expression looks
odd - the => is no longer just an operator linking two expressions, it's
an arbitrary piece of syntax inserted just because it feels like
something should go there (and I agree it does look more readable than
nothing at all).
How about a variant on existing curly braces:
fn($x) { $x * 2 }
lambda($x) { $x * 2 }
Or the arrow inside the parens:
fn($x => $x * 2)
function($x => $x * 2)
My point is that there are plenty of other variants, if we're no longer
mimicking the "(params) => expression" syntax of other languages.
Regards,
--
Rowan Collins
[IMSoP]
On Sat, Sep 26, 2015 at 1:07 PM, Rowan Collins rowan.collins@gmail.com
wrote:
What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.If a keyword is required next to the parameters, having the => as a
separate token looks a bit weird. It no longer matches other languages, so
how about thinking a bit further outside the box?One of the random thoughts that popped into my head during the previous
discussion was to base the syntax on the C for-loop, which would also give
a place for bound variables without the "use" keyword. e.g. your example
could become fn($x;; $x * 2)I picked "lambda" as the keyword before, and gave these examples:
lambda(params; bound vars; expression)
$double = lambda($a;; 2*$a)
$x=3; $triple = lambda($a; $x; $x * $a)function sumEventScores($events, $scores) {
$types = array_map(lambda($event;; $event['type']), $events);
return array_reduce($types, lambda($sum, $type; $scores; $sum +
$scores[$type]));
}Adding in the type information, we'd get this:
lambda(int $sum, string $type; $scores; $sum + $scores[$type])
or with fn() if you prefer:
fn(int $sum, string $type; $scores; $sum + $scores[$type])
If return typehints are also required, I'm not sure where they'd best be
placed. If they're outside the parens, they end up after the expression,
which might look odd:
fn(int $sum, string $type; $scores; $sum + $scores[$type]): intA few other possibilities:
fn(int $sum, string $type; $scores; $sum + $scores[$type]; int)
fn(int $sum, string $type: int; $scores; $sum + $scores[$type])
fn:int(int $sum, string $type; $scores; $sum + $scores[$type])All of this assumes that the shorthand is only available for simple
expressions, not function bodies, but it seems a bit rendundant to allow
both of these:
function($x) { foo(); bar(); baz(); }
fn($x) => { foo(); bar(); baz(); }And only marginally more useful if variables are auto-captured, with all
the downsides of that which have already been raised:
function($x) use ($y) { foo($x); bar($x, $y); }
fn($x) => { foo($x); bar($x, $y); }
I'm leaning toward a compromise between Levi's suggested syntax (which is
unambiguous and short, but auto-closes over parent's scope) and Rowan's
for-loop style (which imports variables but the syntax feels cramped to
me). Example:
$a = 1;
$b = fn($x; $a) => $x + $a; // note the semi-colon here, $a is
explicitly imported
$c = $b(1); // 2
All variables after the semi-colon are imported. The semi-colon and
variable list is optional. This could be extended to function proper,
coexisting with, or deprecating, the use syntax. Example:
Before:
function foo($x) use ($y) { ... }
Becomes:
function foo($x; $y) { ... }
And I think it continues to work with STH:
function bar(string $x, int $y; int $a):int {
$b = strlen($x) + $y + $a;
return fn(int $c; int $b):int => $b + $c;
}
$a = 5;
bar('hello', 1)(2); // lucky number 13
bishop
On Sat, Sep 26, 2015 at 1:07 PM, Rowan Collins rowan.collins@gmail.com
wrote:What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.If a keyword is required next to the parameters, having the => as a
separate token looks a bit weird. It no longer matches other languages, so
how about thinking a bit further outside the box?One of the random thoughts that popped into my head during the previous
discussion was to base the syntax on the C for-loop, which would also give a
place for bound variables without the "use" keyword. e.g. your example could
become fn($x;; $x * 2)I picked "lambda" as the keyword before, and gave these examples:
lambda(params; bound vars; expression)
$double = lambda($a;; 2*$a)
$x=3; $triple = lambda($a; $x; $x * $a)function sumEventScores($events, $scores) {
$types = array_map(lambda($event;; $event['type']), $events);
return array_reduce($types, lambda($sum, $type; $scores; $sum +
$scores[$type]));
}Adding in the type information, we'd get this:
lambda(int $sum, string $type; $scores; $sum + $scores[$type])
or with fn() if you prefer:
fn(int $sum, string $type; $scores; $sum + $scores[$type])
If return typehints are also required, I'm not sure where they'd best be
placed. If they're outside the parens, they end up after the expression,
which might look odd:
fn(int $sum, string $type; $scores; $sum + $scores[$type]): intA few other possibilities:
fn(int $sum, string $type; $scores; $sum + $scores[$type]; int)
fn(int $sum, string $type: int; $scores; $sum + $scores[$type])
fn:int(int $sum, string $type; $scores; $sum + $scores[$type])All of this assumes that the shorthand is only available for simple
expressions, not function bodies, but it seems a bit rendundant to allow
both of these:
function($x) { foo(); bar(); baz(); }
fn($x) => { foo(); bar(); baz(); }And only marginally more useful if variables are auto-captured, with all
the downsides of that which have already been raised:
function($x) use ($y) { foo($x); bar($x, $y); }
fn($x) => { foo($x); bar($x, $y); }I'm leaning toward a compromise between Levi's suggested syntax (which is
unambiguous and short, but auto-closes over parent's scope) and Rowan's
for-loop style (which imports variables but the syntax feels cramped to me).
Example:$a = 1;
$b = fn($x; $a) => $x + $a; // note the semi-colon here, $a is explicitly
imported
$c = $b(1); // 2
I'm going to ask everyone to stop saying that auto-closing is bad
unless they also provide a demonstration of why it was bad.
I'm also going to ask everyone to stop suggesting new syntax for
importing variables. If use() is a pain then auto-importing the used
variables is a good solution. If it's not a pain why are you
suggesting new syntax?
Levi Morrison wrote on 01/10/2015 04:06:
I'm going to ask everyone to stop saying that auto-closing is bad
unless they also provide a demonstration of why it was bad.
Please see my e-mail from last night, or Rasmus's in the original
thread. It is "bad" (or, controversial) because it is a fundamental
change to PHP's scoping rules, which currently treat all variables
denoted by "$foo" as local to the current function, without exception.
I'm also going to ask everyone to stop suggesting new syntax for
importing variables. If use() is a pain then auto-importing the used
variables is a good solution. If it's not a pain why are you
suggesting new syntax?
Because the feature is being described as "short closures", or as a new
syntax, so people are looking for ways to make closures shorter without
changing the functionality.
If auto-capture is the main feature you are interested in, and syntax
just a by-product, then it might be sensible to make that clear. As a
straw man, it would be possible to have auto-capture closures with no
new syntax at all, e.g. function($x) use(*) { return $x * $y; }
It's perfectly reasonable to want both a shorter syntax and a different
capture behaviour; but it also seems perfectly reasonable to want a
shorter syntax with the same functionality as the current one, just as
we have with [] vs array().
Regards,
Rowan Collins
[IMSoP]
On Thu, 01 Oct 2015 13:48:52 +0300, Rowan Collins
rowan.collins@gmail.com wrote:
Levi Morrison wrote on 01/10/2015 04:06:
I'm going to ask everyone to stop saying that auto-closing is bad
unless they also provide a demonstration of why it was bad.Please see my e-mail from last night, or Rasmus's in the original
thread. It is "bad" (or, controversial) because it is a fundamental
change to PHP's scoping rules, which currently treat all variables
denoted by "$foo" as local to the current function, without exception.
And yet we have super-globals. You can also access variables of one
script's global scope from another script's global scope without importing
or anything like that. I wouldn't say that PHP has or follows any
idiomatic "way" of how scopes should work, it's just evolutionary.
I'm also going to ask everyone to stop suggesting new syntax for
importing variables. If use() is a pain then auto-importing the used
variables is a good solution. If it's not a pain why are you
suggesting new syntax?Because the feature is being described as "short closures", or as a new
syntax, so people are looking for ways to make closures shorter without
changing the functionality.If auto-capture is the main feature you are interested in, and syntax
just a by-product, then it might be sensible to make that clear. As a
straw man, it would be possible to have auto-capture closures with no
new syntax at all, e.g. function($x) use(*) { return $x * $y; }
I believe Levi's and Bob's intentions are not to introduce auto-capture
but to add short closures, auto-capture being a tool that works best to
help in achieving this. use(*)
just as long as use($y)
, and the
shorter version where you need to separate args from uses with semicolon
will be harder to read (ex.: fn($x; $y) => $x * $y
vs fn($x) => $x * $y
- it's not obvious at all what semicolon does. Whereas with auto
capture you can at least guess that this variable will be captured from
defining scope, if you don't know how this feature works).
Nikita Nefedov wrote on 01/10/2015 12:07:
And yet we have super-globals. You can also access variables of one
script's global scope from another script's global scope without
importing or anything like that.
You are right, super-globals are an exception to the rule, because they
appear in all function scopes.
An include doesn't change the scope, so I don't think that changes the
rule at all - if you use include() while you're in a function, the
variables all still refer to the function scope, and don't automatically
import or export anything.
I wouldn't say that PHP has or follows any idiomatic "way" of how
scopes should work, it's just evolutionary.
That's not how Rasmus expressed it
[http://marc.info/?l=php-internals&m=144107616411299&w=2]:
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.
If auto-capture is the main feature you are interested in, and syntax
just a by-product, then it might be sensible to make that clear. As a
straw man, it would be possible to have auto-capture closures with no
new syntax at all, e.g. function($x) use(*) { return $x * $y; }I believe Levi's and Bob's intentions are not to introduce
auto-capture but to add short closures, auto-capture being a tool that
works best to help in achieving this.
It is a tool for making them shorter, yes, but it is not the only way,
and it comes with a cost of changing established behaviour. Again, look
at C++'s lambda syntax - it is much shorter than PHP's, but requires a
list of all variables from the outer scope being captured.
There are actually 3 tools being used to make it shorter in the current RFC:
- replacing "function" keyword with syntax elements, or a shorter keyword
- making the "return" keyword implicit (in one variant of the syntax,
the other having a full function body) - eliminating the entire "use" clause by automatically capturing all
used variables
I am suggesting the first two be kept, but replace the third one with:
- shortening the "use" clause by tightening up the syntax
use(*)
just as long asuse($y)
, and the shorter version where you
need to separate args from uses with semicolon will be harder to read
(ex.:fn($x; $y) => $x * $y
vsfn($x) => $x * $y
- it's not
obvious at all what semicolon does.
You've picked on one syntax I explicitly labelled as "a straw man", and
one idea which was proposed by somebody else a few hours ago, and use
those to somehow dismiss the whole idea of a short syntax which lists
captured variables. I have come up with about a dozen different ideas in
various parts of this discussion, and could list dozens more. They would
all have their pros and cons, but I don't see how you can dismiss the
whole concept, UNLESS you consider the auto-capture to be an end in its
own right, rather than "just a way of making it shorter".
Whereas with auto capture you can at least guess that this variable
will be captured from defining scope, if you don't know how this
feature works).
Well, you certainly can't guess it from the behaviour of anything else
in PHP. You could guess based on the behaviour of other languages where
scope works differently, though, I suppose.
Regards,
Rowan Collins
[IMSoP]
On Thu, 01 Oct 2015 15:33:51 +0300, Rowan Collins
rowan.collins@gmail.com wrote:
That's not how Rasmus expressed it
[http://marc.info/?l=php-internals&m=144107616411299&w=2]: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.
Ok, it proves that it's not evolutionary that scopes aren't chained.
Taking my words back.
It is a tool for making them shorter, yes, but it is not the only way,
and it comes with a cost of changing established behaviour. Again, look
at C++'s lambda syntax - it is much shorter than PHP's, but requires a
list of all variables from the outer scope being captured.
C++11 doesn't require the list of all variables, but it does require
explicit "wildcard" ([=] for copy semantics, [&] for capturing by
reference).
But C++ is not the best place for picking up syntax choices, quite frankly
it's one of the least readable languages in a way that it allows you to
write very cryptic code easily (but still allows to write perfectly
readable code as well).
I'm not aware of reasons why ISOCPP decided that automatic capture should
be disabled by default, maybe it was because it was hard to decide whether
it should default to by-value or to by-reference semantics, or maybe []
tokens were needed because of parsing limitations.
You've picked on one syntax I explicitly labelled as "a straw man", and
one idea which was proposed by somebody else a few hours ago, and use
those to somehow dismiss the whole idea of a short syntax which lists
captured variables. I have come up with about a dozen different ideas in
various parts of this discussion, and could list dozens more. They would
all have their pros and cons, but I don't see how you can dismiss the
whole concept, UNLESS you consider the auto-capture to be an end in its
own right, rather than "just a way of making it shorter".
I don't think there was a dozen of different ideas, I could only find
those about lambda(arg-list; use-list; expression)
and variations of it
with different keywords and different return-type syntax.
I do understand that this is quite subjective, but neither this syntax nor
fn(arg-list; use-list) expression
look obvious and easily readable to me.
And one thing that makes auto capture much better choice than explicit
capture (as it've been said a couple of times already) is partial
application:
$mul = fn($x) => fn($y) => fn($z) => $x * $y * $z;
Looks simpler than:
$mul = fn($x) => fn($y; $x) => fn($z; $x, $y) => $x * $y * $z;
I don't think there was a dozen of different ideas, I could only find
those aboutlambda(arg-list; use-list; expression)
and variations of it
with different keywords and different return-type syntax.
I do understand that this is quite subjective, but neither this syntax nor
fn(arg-list; use-list) expression
look obvious and easily readable to me.
It doesn't look obvious because its new syntax. The first time I saw a
for(var; bool; expr) I was totally confused, because I had only seen for
var in range. But you know what? I looked at the docs and then knew how it
worked, not that hard.
And one thing that makes auto capture much better choice than explicit
capture (as it've been said a couple of times already) is partial
application:$mul = fn($x) => fn($y) => fn($z) => $x * $y * $z;
Looks simpler than:
$mul = fn($x) => fn($y; $x) => fn($z; $x, $y) => $x * $y * $z;
Yes it looks simpler, but if this is going to continue being the argument
for auto-capturing can we at least get a real world example? I have never
once in the wild seen a lambda that returns a lambda that returns a lambda,
all using variables from the previous lamba(s). I'm fairly certain no one
will invoke the overhead of a function call 3 times just to multiply 3
numbers together. Please give me a valid example if you want me to believe
this to be a valid argument.
There is no way in php that I know of to get access to a parent's scope,
unless explicitly used which is only available on lambda/closures.
Therefore, automatic closing of the variables is very much against the
scoping rules of the language. It is totally unexpected for someone who has
been using the language for many years.
And one thing that makes auto capture much better choice than explicit
capture (as it've been said a couple of times already) is partial
application:$mul = fn($x) => fn($y) => fn($z) => $x * $y * $z;
Looks simpler than:
$mul = fn($x) => fn($y; $x) => fn($z; $x, $y) => $x * $y * $z;
Yes it looks simpler, but if this is going to continue being the argument
for auto-capturing can we at least get a real world example? I have never
once in the wild seen a lambda that returns a lambda that returns a lambda,
all using variables from the previous lamba(s). I'm fairly certain no one
will invoke the overhead of a function call 3 times just to multiply 3
numbers together. Please give me a valid example if you want me to believe
this to be a valid argument.
This is close: https://github.com/morrisonlevi/Algorithm/blob/master/src/reduce.php
Levi Morrison wrote on 01/10/2015 16:52:
This is close:https://github.com/morrisonlevi/Algorithm/blob/master/src/reduce.php
When would you store or pass around the intermediate callback (the
result of reduce($initial)) without immediately invoking it with a
callback? If you just run "reduce($initial)($my_fn)($data)", it seems no
more useful than "reduce(42, $my_fn, $data)", which would be more
efficient internally.
If anything, I can imagine wanting to write $my_reduce = reduce($my_fn)
and then $my_reduce($initial, $data), but that's still 2 steps rather
than 3.
Genuine question, not a criticism - this functional(?) style of
composition is not something I'm very familiar with. (For the same
reason, I struggle to read that function definition no matter which
syntax it's expressed in.)
Regards,
Rowan Collins
[IMSoP]
Levi Morrison wrote on 01/10/2015 16:52:
This is
close:https://github.com/morrisonlevi/Algorithm/blob/master/src/reduce.phpWhen would you store or pass around the intermediate callback (the result of
reduce($initial)) without immediately invoking it with a callback? If you
just run "reduce($initial)($my_fn)($data)", it seems no more useful than
"reduce(42, $my_fn, $data)", which would be more efficient internally.If anything, I can imagine wanting to write $my_reduce = reduce($my_fn) and
then $my_reduce($initial, $data), but that's still 2 steps rather than 3.Genuine question, not a criticism - this functional(?) style of composition
is not something I'm very familiar with. (For the same reason, I struggle to
read that function definition no matter which syntax it's expressed in.)
I've worked out what I think are elegant definitions that are highly
composable and usable by iterators and arrays alike. For example, it's
common in our code base to do a filter followed by a map (or series of
maps) and sometimes followed by a reduce. Here's a simplified example,
with definitions taken from the linked repository:
$algorithm = chain(
filter(function($x) => $x % 2 > 0),
map(function($x) => $x * 2),
sum()
);
$algorithm([1,2,3]);
If you understand the (very common) algorithms filter and map then
this is trivial to understand. More than that, it's easy to write and
very difficult to introduce bugs into it since you are writing very
simple functions that perform a single task.
Chain works is because the functions passed to it accept iterables as
their only parameter and return either an another iterable or a
reducing function (in this example sum is the reducer). This is why
the functions are returning closures that accept only one parameter.
This can be done with a binding function with more traditional
definitions like Nikita Popov's iter library, or by writing inline
closures:
$algorithm = chain(
// using a bind function
bind('iter\\fn\\filter', function($x) => $x % 2 > 0),
bind('iter\\fn\\map', function($x) => $x * 2),
// using a closure (showed the closure with this one because
// the parameter order doesn't work well with bind)
function($input) => iter\fn\reduce(function($x, $y) => $x +
$y, $input, 0)
);
$algorithm([1,2,3]);
There's a lot more noise here, which is why the library I linked
returns a series of closures. Both are valid, but if you are going to
write this particular style then the closure style has less noise when
called.
Hopefully that was helpful.
Levi Morrison wrote on 01/10/2015 18:38:
Chain works is because the functions passed to it accept iterables as
their only parameter and return either an another iterable or a
reducing function (in this example sum is the reducer). This is why
the functions are returning closures that accept only one parameter.
OK, that makes sense of one level of indirection. It doesn't however
seem to justify the nesting in reduce()
If I understand correctly, your library obliges me to write this:
$algorithm = chain(
map(function($x) => $x * 2),
reduce(0)(function($acc, $val) => $acc + $val)
);
$algorithm([1,2,3]);
But that seems to have no advantage over this:
$algorithm = chain(
map(function($x) => $x * 2),
reduce(0, function($acc, $val) => $acc + $val)
);
$algorithm([1,2,3]);
Which would make the body of reduce significantly simpler, because it's
just one named function accepting a closure, and returning a closure,
with no further nesting.
The function chaining only appears to be useful if the closure returned
by reduce(0) is to be passed or applied in some other way, and I can't
think of when that would be required.
Regards,
Rowan Collins
[IMSoP]
Nikita and all,
On Thu, 01 Oct 2015 15:33:51 +0300, Rowan Collins rowan.collins@gmail.com
wrote:That's not how Rasmus expressed it
[http://marc.info/?l=php-internals&m=144107616411299&w=2]: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.Ok, it proves that it's not evolutionary that scopes aren't chained. Taking
my words back.It is a tool for making them shorter, yes, but it is not the only way, and
it comes with a cost of changing established behaviour. Again, look at C++'s
lambda syntax - it is much shorter than PHP's, but requires a list of all
variables from the outer scope being captured.C++11 doesn't require the list of all variables, but it does require
explicit "wildcard" ([=] for copy semantics, [&] for capturing by
reference).
But C++ is not the best place for picking up syntax choices, quite frankly
it's one of the least readable languages in a way that it allows you to
write very cryptic code easily (but still allows to write perfectly readable
code as well).
I'm not aware of reasons why ISOCPP decided that automatic capture should be
disabled by default, maybe it was because it was hard to decide whether it
should default to by-value or to by-reference semantics, or maybe [] tokens
were needed because of parsing limitations.You've picked on one syntax I explicitly labelled as "a straw man", and
one idea which was proposed by somebody else a few hours ago, and use those
to somehow dismiss the whole idea of a short syntax which lists captured
variables. I have come up with about a dozen different ideas in various
parts of this discussion, and could list dozens more. They would all have
their pros and cons, but I don't see how you can dismiss the whole concept,
UNLESS you consider the auto-capture to be an end in its own right, rather
than "just a way of making it shorter".I don't think there was a dozen of different ideas, I could only find those
aboutlambda(arg-list; use-list; expression)
and variations of it with
different keywords and different return-type syntax.
I do understand that this is quite subjective, but neither this syntax nor
fn(arg-list; use-list) expression
look obvious and easily readable to me.
The problem is that semicolons are non-obvious, especially in a
context where commas are used (and traditionally used).
Example, tell the difference quickly between
fn($a, $b; $c) => $a + $b + $c;
and
fn($a; $b, $c) => $a + $b + $c.
At a glance, they are identical. You have to actually look at each
item to realize that there's a difference. At least with use() there's
a syntactical separator there to draw your eye to the contextually
relevant information.
On Thu, Oct 1, 2015 at 12:28 PM, Anthony Ferrara ircmaxell@gmail.com
wrote:
Nikita and all,
I don't think there was a dozen of different ideas, I could only find
those
aboutlambda(arg-list; use-list; expression)
and variations of it with
different keywords and different return-type syntax.
I do understand that this is quite subjective, but neither this syntax
nor
fn(arg-list; use-list) expression
look obvious and easily readable to
me.The problem is that semicolons are non-obvious, especially in a
context where commas are used (and traditionally used).Example, tell the difference quickly between
fn($a, $b; $c) => $a + $b + $c;
and
fn($a; $b, $c) => $a + $b + $c.
At a glance, they are identical. You have to actually look at each
item to realize that there's a difference. At least with use() there's
a syntactical separator there to draw your eye to the contextually
relevant information.
True. But a developer can mitigate with judicious white-space:
fn($a, $b ; $c) => $a+$b+$c
Or we can figure out some other such symbol. Worse casing no white space,
brain storming:
fn($a,$b:$c) => $a+$b+$c // not much better
fn($a,$b!$c) => $a+$b+$c // better, but looks like not
fn($a,$b&$c) => $a+$b+$c // lost in the noise, looks like bitwise
fn($a,$b%$c) => $a+$b+$c // perl jibberish, looks modulo
fn($a,$b--$c) => $a+$b+$c // multi-char, looks like decrement
fn($a,$b::$c) => $a+$b+$c // maybe, kinda confusing
// my favorite
fn($a, $b @ $c) => $a + $b + $c;
// vs.
function ($a, $b) use ($c) { return $a + $b + $c; }
// a space savings of 100%
// unambiguous parsing
// no backtracing
// no new rules to learn about scope pulling
// works if we later support '*' for scope pulling
Or we can figure out some other such symbol. Worse casing no white space,
brain storming:
Please, I already asked people to stop making suggestions for shorter
syntax for use()
. Again, if use() is a pain then auto-importing the
used variables is a good solution. If it's not a pain why are you
suggesting new syntax?
Levi Morrison wrote on 01/10/2015 18:40:
Or we can figure out some other such symbol. Worse casing no white space,
brain storming:
Please, I already asked people to stop making suggestions for shorter
syntax foruse()
.
I tried to move the discussion to whether or not auto-capturing
variables is a good idea, regardless of syntax, but the responses
continue to be of the form "but that particular example syntax looks a
bit ugly".
So I agree, let's not discuss the details of ANY syntax, until we've
come to some conclusion on whether or not auto-capture is a negotiable
feature.
Again, if use() is a pain then auto-importing the
used variables is a good solution. If it's not a pain why are you
suggesting new syntax?
I thought I'd answered this already - because some people dislike the
verbosity of "use" (and of the syntax in general) but are happy with
the functionality. You are assuming that "I think the current syntax
is verbose" is synonymous with "I think having to list captured
variables is annoying", but there are people who do not take that position.
Regards,
Rowan Collins
Levi Morrison wrote on 01/10/2015 18:40:
Or we can figure out some other such symbol. Worse casing no white space,
brain storming:Please, I already asked people to stop making suggestions for shorter
syntax foruse()
.I tried to move the discussion to whether or not auto-capturing variables is
a good idea, regardless of syntax, but the responses continue to be of the
form "but that particular example syntax looks a bit ugly".So I agree, let's not discuss the details of ANY syntax, until we've come to
some conclusion on whether or not auto-capture is a negotiable feature.
It's absolutely negotiable. I don't need to defend this anymore – look
at the number of yes votes Bob's proposal garnered despite some very
glaring concerns about other aspects of the RFC.
Again, if use() is a pain then auto-importing the
used variables is a good solution. If it's not a pain why are you
suggesting new syntax?I thought I'd answered this already - because some people dislike the
verbosity of "use" (and of the syntax in general) but are happy with the
functionality. You are assuming that "I think the current syntax is
verbose" is synonymous with "I think having to list captured variables is
annoying", but there are people who do not take that position.
You are capped to saving about 5-7 characters no matter what you do
with this functionality. This is nothing compared to what is proposed
if you save just one single-letter variable import with fairly
customary spacing:
$versionA = function($x) => $x + $y;
$versionB = function($x) use($y) {
return $x + $y;
};
That's a 27 character difference (well, 24 if you use tab instead of
spaces). Twenty-seven. Please think about that before you respond
again.
Levi Morrison wrote on 01/10/2015 19:09:
You are capped to saving about 5-7 characters no matter what you do
with this functionality.
I see an upper limit to savings of 21 - using "|" to stand in for "some
single character delimiting the sections" gives:
|$x|$y|$x*$y| // 13 chars
vs
function($x)use($y){return $x*$y;} // 34 chars
Or we can figure out some other such symbol. Worse casing no white space,
brain storming:Please, I already asked people to stop making suggestions for shorter
syntax foruse()
. Again, if use() is a pain then auto-importing the
used variables is a good solution. If it's not a pain why are you
suggesting new syntax?
In PHP, I expect to "use" external variables in my closure. I'm taking no
stance on whether that's generally a good or bad thing to do in any
particular language. I'm saying that, in PHP, that's how its done. It's
different from other languages, which can be confusing, as online
documentation comments show 1. But once you've RTM and learned it, you
stop thinking about it. It's just what you do.
Now enter auto-importing, which is a great solution... were it not for
precedent, everything else I've learned about closures in PHP, standing in
the way. Auto-importing lets me trade convenience of typing fewer
characters for a higher cognitive load. I have to remember that PHP
behaves differently when I type function vs. fn even though the outcome is
the same. I don't consider that a win for the language, because it will
cascade to exceptions in the documentation, plenty of SO questions, and
probably a PHP Sadness.
What I want is a one-to-one behavior mapping between long syntax and
short. The syntax suggestions put out on the list attempt to reconcile a
great feature with an earnest belief that language consistency trumps
typing convenience.
Den 2015-10-01 kl. 19:12, skrev Bishop Bettini:
On Thu, Oct 1, 2015 at 12:28 PM, Anthony Ferrara ircmaxell@gmail.com
wrote:Nikita and all,
I don't think there was a dozen of different ideas, I could only find
those
aboutlambda(arg-list; use-list; expression)
and variations of it with
different keywords and different return-type syntax.
I do understand that this is quite subjective, but neither this syntax
nor
fn(arg-list; use-list) expression
look obvious and easily readable to
me.The problem is that semicolons are non-obvious, especially in a
context where commas are used (and traditionally used).Example, tell the difference quickly between
fn($a, $b; $c) => $a + $b + $c;
and
fn($a; $b, $c) => $a + $b + $c.
At a glance, they are identical. You have to actually look at each
item to realize that there's a difference. At least with use() there's
a syntactical separator there to draw your eye to the contextually
relevant information.
True. But a developer can mitigate with judicious white-space:fn($a, $b ; $c) => $a+$b+$c
Or we can figure out some other such symbol. Worse casing no white space,
brain storming:fn($a,$b:$c) => $a+$b+$c // not much better
fn($a,$b!$c) => $a+$b+$c // better, but looks like not
fn($a,$b&$c) => $a+$b+$c // lost in the noise, looks like bitwise
fn($a,$b%$c) => $a+$b+$c // perl jibberish, looks modulo
fn($a,$b--$c) => $a+$b+$c // multi-char, looks like decrement
fn($a,$b::$c) => $a+$b+$c // maybe, kinda confusing// my favorite
fn($a, $b @ $c) => $a + $b + $c;
Would it be: fn($a, $b @ $c, $d) => $a + $b + $c + $d;
with several parameters? And if one could solve the
parser problem it could be with typehint & a default
value either:
(int $a = 10, int $b @ $c, $d) => $a + $b + $c + $d;
or
(int $a = 10, int $b @ $c, $d) ==> $a + $b + $c + $d;
or
(int $a = 10, int $b @ $c, $d) ~> $a + $b + $c + $d;
Regards //Björn
Nikita Nefedov wrote on 01/10/2015 15:58:
On Thu, 01 Oct 2015 15:33:51 +0300, Rowan Collins
rowan.collins@gmail.com wrote:It is a tool for making them shorter, yes, but it is not the only
way, and it comes with a cost of changing established behaviour.
Again, look at C++'s lambda syntax - it is much shorter than PHP's,
but requires a list of all variables from the outer scope being
captured.C++11 doesn't require the list of all variables, but it does require
explicit "wildcard" ([=] for copy semantics, [&] for capturing by
reference).
I'm not actually that familiar with C++'s lambdas, and was basing my
understanding on this page:
https://msdn.microsoft.com/en-us/library/dd293608.aspx
I read this line as implying the same behaviour as PHP's:
An empty capture clause, [ ], indicates that the body of the lambda
expression accesses no variables in the enclosing scope.
But on closer inspection, the meaning of [=] on its own is just not
terribly clear on that particular description, so I stand corrected.
But C++ is not the best place for picking up syntax choices, quite
frankly it's one of the least readable languages in a way that it
allows you to write very cryptic code easily (but still allows to
write perfectly readable code as well).
Sure, short syntax leads to hard-to-read code. A good argument to stick
to the existing PHP verbosity.
And, again, I was not advocating C++'s exact syntax (it conflicts with
existing syntax anyway), merely showing that "short" and "explicitly
listed captures" are not mutually exclusive.
I don't think there was a dozen of different ideas, I could only find
those aboutlambda(arg-list; use-list; expression)
and variations of
it with different keywords and different return-type syntax.
Fair enough. But there's no point me addressing bikeshedding details
about semi-colon vs other punctuation, etc, etc, if you are
fundamentally opposed to requiring an explicit list of used variables.
And one thing that makes auto capture much better choice than explicit
capture (as it've been said a couple of times already) is partial
application:$mul = fn($x) => fn($y) => fn($z) => $x * $y * $z;
Looks simpler than:
$mul = fn($x) => fn($y; $x) => fn($z; $x, $y) => $x * $y * $z;
It looks simpler, but it's an illusion - there is actually a closure
hidden in there that has argument ($z) and captures ($x, $y). To someone
not used to chaining, and not expecting scopes to be inherited, it's far
from simple to follow where $x and $y come from and end up.
Let's pick another syntax - again, completely off the top of my head,
may not even be possible:
{ args|captures => return }
$double = { $x => $x * 2 }
$mul = {$x => {$y|$x => {$z|$x,$y => $x * $y * $z} } }
Note that the nesting is indicated explicitly, rather than via
associativity, the captured variables are listed explicitly, but clearly
separated from the argument list.
So my question is: if a short syntax was suggested which required
explicit capture but otherwise seemed compact and elegant, would you be
interested, or do you consider auto-capture a non-negotiable feature?
Regards,
Rowan Collins
[IMSoP]
(Email in gist format:
https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c)In EcmaScript 2015 (ES6) the expression
(x) => x * 2
means to create
an anonymous function with one parameterx
that will returnx * 2
.
For example:(x) => x * 2 // is equivalent to: function(x) { return x * 2; }
A modified example from documentation by Mozilla Developer
Network page demonstrates how they are useful:var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(function(s){ return s.length }); // pre-ES6 var a3 = a.map((s) => s.length); // ES6
There has been some talk about how we can use arrow function
expressions in PHP. In PHP using the same syntax would have some
ambiguities:// Does this mean: // 1. Create an array key with the result of `($x)` and a value
with
$x * 2
// 2. Create an array with one value that is an anonymous function
[($x) => $x * 2]// Does this mean: // 1. Yield a key with the result of `($x)` and a value with `$x * 2` // 2. Yield an anonymous function yield ($x) => $x * 2;
This is why Bob Weinand proposed using
~>
instead of=>
.
However, if we allow type declarations there is another issue. In the
definition(Type &$x) => expr
the(Type &$var)
part can parse as
"take constantType
and variable$var
and do a bitwise and&
operation." After that the=>
will be an unexpected token. Even
though the rule would be invalid the parser doesn't know that far
ahead it will error and it doesn't know which rule to pick. Changing
the token from=>
to~>
doesn't affect this issue.We could solve the first ambiguities with prefering the current
meaning withkey => value
and requiring the meaning with closures to
wrap them in()
. We could solve the latter ambiguity with a
backtracking parser since it will eventually error and then know to
pick the other rule. However, I really think this is a bad idea.So how can we have shorter closures without this mess? One simple way
is to require thefunction
prefix:// clearly an array with an anonymous function [function($x) => $x * 2]; // clearly yields an anonymous function yield function($x) => $x * 2; // clearly an anonymous function function(Type &$x) => expr;
Requiring the
function
prefix mitigates one of the value parts of
arrow functions: they are short.Another option would be to resolve the ambiguities with keys and
values but to change the type information in parameters:(&$input: array) => expr
By putting the type after the variable (similar to how we declare
return types) we no longer have the issues with mis-parsing. Of
course, that's not how we declare parameter types currently. I think
we would need to permit it everywhere and deprecate the current syntax
with the type being prefixed. (By deprecate I mean in PHP 8 and not
remove it until PHP 9 or later)I would prefer that we shorten the
function
keyword tofn
:[fn($x) => $x * 2]
This preserves the shortness of the expression while providing
unambiguous, simple parsing. Of course, now we have a similar issue:
we have bothfn
andfunction
.What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.
Bumping. I feel like perhaps people aren't aware that this is an
important conversation to have right now before the vote on
short_closures end. This is because depending on the interpretation of
the rules it may not be possible to have another vote on the short
closure idea for at least 6 months.
(Email in gist format:
https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c)In EcmaScript 2015 (ES6) the expression
(x) => x * 2
means to create
an anonymous function with one parameterx
that will returnx * 2
.
For example:(x) => x * 2 // is equivalent to: function(x) { return x * 2; }
A modified example from documentation by Mozilla Developer
Network page demonstrates how they are useful:var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(function(s){ return s.length }); // pre-ES6 var a3 = a.map((s) => s.length); // ES6
There has been some talk about how we can use arrow function
expressions in PHP. In PHP using the same syntax would have some
ambiguities:// Does this mean: // 1. Create an array key with the result of `($x)` and a value
with
$x * 2
// 2. Create an array with one value that is an anonymous function
[($x) => $x * 2]// Does this mean: // 1. Yield a key with the result of `($x)` and a value with `$x * 2` // 2. Yield an anonymous function yield ($x) => $x * 2;
This is why Bob Weinand proposed using
~>
instead of=>
.
However, if we allow type declarations there is another issue. In the
definition(Type &$x) => expr
the(Type &$var)
part can parse as
"take constantType
and variable$var
and do a bitwise and&
operation." After that the=>
will be an unexpected token. Even
though the rule would be invalid the parser doesn't know that far
ahead it will error and it doesn't know which rule to pick. Changing
the token from=>
to~>
doesn't affect this issue.We could solve the first ambiguities with prefering the current
meaning withkey => value
and requiring the meaning with closures to
wrap them in()
. We could solve the latter ambiguity with a
backtracking parser since it will eventually error and then know to
pick the other rule. However, I really think this is a bad idea.So how can we have shorter closures without this mess? One simple way
is to require thefunction
prefix:// clearly an array with an anonymous function [function($x) => $x * 2]; // clearly yields an anonymous function yield function($x) => $x * 2; // clearly an anonymous function function(Type &$x) => expr;
Requiring the
function
prefix mitigates one of the value parts of
arrow functions: they are short.Another option would be to resolve the ambiguities with keys and
values but to change the type information in parameters:(&$input: array) => expr
By putting the type after the variable (similar to how we declare
return types) we no longer have the issues with mis-parsing. Of
course, that's not how we declare parameter types currently. I think
we would need to permit it everywhere and deprecate the current syntax
with the type being prefixed. (By deprecate I mean in PHP 8 and not
remove it until PHP 9 or later)I would prefer that we shorten the
function
keyword tofn
:[fn($x) => $x * 2]
This preserves the shortness of the expression while providing
unambiguous, simple parsing. Of course, now we have a similar issue:
we have bothfn
andfunction
.What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.--
Hello,
personally, as a purely userland dev, I feel that the correct syntax
would be the "simplest" one - and that's imho the (Type &$x) ==> expr
Or the version with ~> operator - I don't care which one gets in,
althought I would rather see the ==> one. :)
I understand that there might be some ambiguity in the parser that
would need to be solved by backtracking, but for using the language,
the "simplest" option should IMHO be the correct one. Also not
introducing any more "stuff" (like new parameter types syntax) would
be a plus.
For the need to have a single parameter enclosed with ( ) - by
thinking more and more about it, I think that having the one special
case for not requiring parenthesis around parameters is pretty uselss,
since it would have to be there anyways if you wrote the typehint.
PS: the [fn($x) => $x * 2] seems ambigous, from reader's POV; key of
the item is result of fn($x) and value is $x * 2? Also, it would be a
huge BC break with not allowing you to name functions fn(), wouldn't
it?
--
Regards
Pavel Kouřil
(Email in gist format:
https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c)In EcmaScript 2015 (ES6) the expression
(x) => x * 2
means to create
an anonymous function with one parameterx
that will returnx * 2
.
For example:(x) => x * 2 // is equivalent to: function(x) { return x * 2; }
A modified example from documentation by Mozilla Developer
Network page demonstrates how they are useful:var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(function(s){ return s.length }); // pre-ES6 var a3 = a.map((s) => s.length); // ES6
There has been some talk about how we can use arrow function
expressions in PHP. In PHP using the same syntax would have some
ambiguities:// Does this mean: // 1. Create an array key with the result of `($x)` and a value
with
$x * 2
// 2. Create an array with one value that is an anonymous function
[($x) => $x * 2]// Does this mean: // 1. Yield a key with the result of `($x)` and a value with `$x * 2` // 2. Yield an anonymous function yield ($x) => $x * 2;
This is why Bob Weinand proposed using
~>
instead of=>
.
However, if we allow type declarations there is another issue. In the
definition(Type &$x) => expr
the(Type &$var)
part can parse as
"take constantType
and variable$var
and do a bitwise and&
operation." After that the=>
will be an unexpected token. Even
though the rule would be invalid the parser doesn't know that far
ahead it will error and it doesn't know which rule to pick. Changing
the token from=>
to~>
doesn't affect this issue.We could solve the first ambiguities with prefering the current
meaning withkey => value
and requiring the meaning with closures to
wrap them in()
. We could solve the latter ambiguity with a
backtracking parser since it will eventually error and then know to
pick the other rule. However, I really think this is a bad idea.So how can we have shorter closures without this mess? One simple way
is to require thefunction
prefix:// clearly an array with an anonymous function [function($x) => $x * 2]; // clearly yields an anonymous function yield function($x) => $x * 2; // clearly an anonymous function function(Type &$x) => expr;
Requiring the
function
prefix mitigates one of the value parts of
arrow functions: they are short.Another option would be to resolve the ambiguities with keys and
values but to change the type information in parameters:(&$input: array) => expr
By putting the type after the variable (similar to how we declare
return types) we no longer have the issues with mis-parsing. Of
course, that's not how we declare parameter types currently. I think
we would need to permit it everywhere and deprecate the current syntax
with the type being prefixed. (By deprecate I mean in PHP 8 and not
remove it until PHP 9 or later)I would prefer that we shorten the
function
keyword tofn
:[fn($x) => $x * 2]
This preserves the shortness of the expression while providing
unambiguous, simple parsing. Of course, now we have a similar issue:
we have bothfn
andfunction
.What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.--
Hello,
personally, as a purely userland dev, I feel that the correct syntax
would be the "simplest" one - and that's imho the (Type &$x) ==> exprOr the version with ~> operator - I don't care which one gets in,
althought I would rather see the ==> one. :)I understand that there might be some ambiguity in the parser that
would need to be solved by backtracking, but for using the language,
the "simplest" option should IMHO be the correct one. Also not
introducing any more "stuff" (like new parameter types syntax) would
be a plus.For the need to have a single parameter enclosed with ( ) - by
thinking more and more about it, I think that having the one special
case for not requiring parenthesis around parameters is pretty uselss,
since it would have to be there anyways if you wrote the typehint.
I do not think it is feasible to make the parser do backtracking or
anything of that sort. How do others feel?
PS: the [fn($x) => $x * 2] seems ambigous, from reader's POV; key of
the item is result of fn($x) and value is $x * 2? Also, it would be a
huge BC break with not allowing you to name functions fn(), wouldn't
it?
This is not a huge backwards compatibility break, no. It is a small
break. This is one reason fn
is not necessarily what I will be
proposing and want to hear more feedback. Note that the function
version wouldn't be possible to be confused at all:
// function($x) isn't allowed for function name
// so this is very unambiguous
[function($x) => $x * 2];
Also note that using closures in array literals is not really that common.
I do not think it is feasible to make the parser do backtracking or
anything of that sort. How do others feel?PS: the [fn($x) => $x * 2] seems ambigous, from reader's POV; key of
the item is result of fn($x) and value is $x * 2? Also, it would be a
huge BC break with not allowing you to name functions fn(), wouldn't
it?This is not a huge backwards compatibility break, no. It is a small
break. This is one reasonfn
is not necessarily what I will be
proposing and want to hear more feedback. Note that thefunction
version wouldn't be possible to be confused at all:// function($x) isn't allowed for function name // so this is very unambiguous [function($x) => $x * 2];
Also note that using closures in array literals is not really that common.
When you choose the function($x) ~> $x * 2 (or with ==> or =>
operator), you end up saving around 5 or 6 characters from the "long
declaration", ending up with "not-so-short closures" instead. You save
a little bit more if you have the implicit "use", but that's one thing
people are torn about, by looking at the discussion about the RFC, but
I think it's too long, for "short closures".
Would making the parser do some backtracking be really that bad -
especially since the option would end up with the IMHO most convenient
option for actual users of the feature?
I do not think it is feasible to make the parser do backtracking or
anything of that sort. How do others feel?PS: the [fn($x) => $x * 2] seems ambigous, from reader's POV; key of
the item is result of fn($x) and value is $x * 2? Also, it would be a
huge BC break with not allowing you to name functions fn(), wouldn't
it?This is not a huge backwards compatibility break, no. It is a small
break. This is one reasonfn
is not necessarily what I will be
proposing and want to hear more feedback. Note that thefunction
version wouldn't be possible to be confused at all:// function($x) isn't allowed for function name // so this is very unambiguous [function($x) => $x * 2];
Also note that using closures in array literals is not really that common.
When you choose the function($x) ~> $x * 2 (or with ==> or =>
operator), you end up saving around 5 or 6 characters from the "long
declaration", ending up with "not-so-short closures" instead. You save
a little bit more if you have the implicit "use", but that's one thing
people are torn about, by looking at the discussion about the RFC, but
I think it's too long, for "short closures".
Reading over the list I don't think people "are torn about" it. There
are some detractors, sure, but there seem to be more detractors about
symbol choice (~) and lack of type declarations.
Would making the parser do some backtracking be really that bad -
especially since the option would end up with the IMHO most convenient
option for actual users of the feature?
I disagree that (int $x) => $x * 2
is vastly better than fn (int $x) => $x * 2
. It might be better but so much better we should
introduce backtracking into it? In my opinion that is very clear: no.
Especially because this also makes it harder to write other tools for
PHP.
Levi Morrison wrote on 29/09/2015 22:55:
When you choose the function($x) ~> $x * 2 (or with ==> or =>
operator), you end up saving around 5 or 6 characters from the "long
declaration", ending up with "not-so-short closures" instead. You save
a little bit more if you have the implicit "use", but that's one thing
people are torn about, by looking at the discussion about the RFC, but
I think it's too long, for "short closures".
Reading over the list I don't think people "are torn about" it. There
are some detractors, sure, but there seem to be more detractors about
symbol choice (~) and lack of type declarations.
On a quick glance through the list I gathered the following names
expressing disapproval of the automatic capture (apologies if I've
misrepresented anyone's opinion here, it was a very quick skim through!):
Dmitry
Rasmus
Peter Petermann
Bishop Bettini
Stas
Ferenc
Thomas Punt
Plus a couple of other people on the fence on the issue (as am I):
Marco Pivetta: "I am still conflicted..."
Arne Blankerts: "I can see the benefit ... but consider it a break of
concepts (habit?) from everywhere else in PHP."
That seems like a significant proportion of the people actively
participating in the thread to me; as mentioned elsewhere, there are
more people who've voted no than have expressed their reasons on the
list, and it's a mug's game to guess which factors influence whom. Also,
remember that the votes on RFCs are intended to confirm consensus, not
replace it - the idea is not to win over just enough people to "win", so
it's not necessarily about numbers.
That's not to say that automatic capture is an absolute blocker, but I
think "the internals community is divided on it" is a reasonable summary.
Regards,
Rowan Collins
[IMSoP]
Hi,
2015-09-30 8:10 GMT-03:00 Rowan Collins rowan.collins@gmail.com:
Levi Morrison wrote on 29/09/2015 22:55:
When you choose the function($x) ~> $x * 2 (or with ==> or =>
operator), you end up saving around 5 or 6 characters from the "long
declaration", ending up with "not-so-short closures" instead. You save
a little bit more if you have the implicit "use", but that's one thing
people are torn about, by looking at the discussion about the RFC, but
I think it's too long, for "short closures".Reading over the list I don't think people "are torn about" it. There
are some detractors, sure, but there seem to be more detractors about
symbol choice (~) and lack of type declarations.On a quick glance through the list I gathered the following names expressing
disapproval of the automatic capture (apologies if I've misrepresented
anyone's opinion here, it was a very quick skim through!):Dmitry
Rasmus
Peter Petermann
Bishop Bettini
Stas
Ferenc
Thomas PuntPlus a couple of other people on the fence on the issue (as am I):
Marco Pivetta: "I am still conflicted..."
Arne Blankerts: "I can see the benefit ... but consider it a break of
concepts (habit?) from everywhere else in PHP."That seems like a significant proportion of the people actively
participating in the thread to me; as mentioned elsewhere, there are more
people who've voted no than have expressed their reasons on the list, and
it's a mug's game to guess which factors influence whom. Also, remember that
the votes on RFCs are intended to confirm consensus, not replace it - the
idea is not to win over just enough people to "win", so it's not necessarily
about numbers.That's not to say that automatic capture is an absolute blocker, but I think
"the internals community is divided on it" is a reasonable summary.
That's the part I couldn't comprehend in the discussion. Auto import
is basically the most interesting reason to have short closures on
PHP. Apart from syntax sugar which, could be nice but, on it's own
wouldn't be as important to most people.
Regards,
Rowan Collins
[IMSoP]
I abstained from voting on Bob's proposal because of the syntax choice
~> and because of the lack of types support. With that said, I think
Levi's proposal is more complete without hurting our current
expectations regarding deterministic parsing. Levi's proposal also
open doors for a short method declaration as "fn" could become
interchangeable with "function" (if we choose to propose the "fn"
prefix).
I still have a personal preference for the '==>' because it looks
less ambiguous on the array edge case '[fn($x) => $x * 2]' vs '[fn($x)
==> $x * 2]', and of course because it is more efficient to grep. But
that preference wouldn't be enough for me to reject the RFC with '=>'.
Ty,
Marcio
Marcio Almada wrote on 30/09/2015 15:49:
That's not to say that automatic capture is an absolute blocker, but I think
"the internals community is divided on it" is a reasonable summary.
That's the part I couldn't comprehend in the discussion. Auto import
is basically the most interesting reason to have short closures on
PHP.
It's also the biggest departure from existing PHP semantics - PHP's
scoping rules are remarkably simple compared to other languages:
everything is local unless qualified. No scope chains like JS, no "my"
and "our" like Perl, no short-hand references to properties of "this"
like Java, etc. "use", "global", and "static" can all be seen as
explicitly importing variables into that local scope.
Breaking that rule is something that needs to be considered very
carefully, and is a lot more than a minor detail of syntax.
I note that C++11, although it has a very compact (and keyword-less)
lambda syntax, still requires an explicit list of captured variables;
unfortunately, we can't just copy that syntax either, because we've used
[$var] for array construction. But it does show that "short closures"
and "auto-capture closures" are two different features; we could save up
to 17 characters without changing behaviour if we could eliminate the
keywords "function", "use", and "return".
Regards,
Rowan Collins
[IMSoP]
Reading over the list I don't think people "are torn about" it. There
are some detractors, sure, but there seem to be more detractors about
symbol choice (~) and lack of type declarations.
Personally, I feel that without the auto import, the feature would be
totally useless. But yes, more people are not happy with the ~ choice.
I disagree that
(int $x) => $x * 2
is vastly better thanfn (int $x) => $x * 2
. It might be better but so much better we should
introduce backtracking into it? In my opinion that is very clear: no.
Especially because this also makes it harder to write other tools for
PHP.
The issue I have with the fn() prefix is that it looks "weird" in case
you are chaining multiples. And it looks like a function call, but it
isn't one.
fn($x) => fn($y) => fn($z)
How does hack solve this? Do they have backtracking in their parser?
Den 2015-09-28 kl. 23:38, skrev Pavel Kouřil:
(Email in gist format:
https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c)In EcmaScript 2015 (ES6) the expression
(x) => x * 2
means to create
an anonymous function with one parameterx
that will returnx * 2
.
For example:(x) => x * 2 // is equivalent to: function(x) { return x * 2; }
A modified example from documentation by Mozilla Developer
Network page demonstrates how they are useful:var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(function(s){ return s.length }); // pre-ES6 var a3 = a.map((s) => s.length); // ES6
There has been some talk about how we can use arrow function
expressions in PHP. In PHP using the same syntax would have some
ambiguities:// Does this mean: // 1. Create an array key with the result of `($x)` and a value
with
$x * 2
// 2. Create an array with one value that is an anonymous function
[($x) => $x * 2]// Does this mean: // 1. Yield a key with the result of `($x)` and a value with `$x * 2` // 2. Yield an anonymous function yield ($x) => $x * 2;
This is why Bob Weinand proposed using
~>
instead of=>
.
However, if we allow type declarations there is another issue. In the
definition(Type &$x) => expr
the(Type &$var)
part can parse as
"take constantType
and variable$var
and do a bitwise and&
operation." After that the=>
will be an unexpected token. Even
though the rule would be invalid the parser doesn't know that far
ahead it will error and it doesn't know which rule to pick. Changing
the token from=>
to~>
doesn't affect this issue.We could solve the first ambiguities with prefering the current
meaning withkey => value
and requiring the meaning with closures to
wrap them in()
. We could solve the latter ambiguity with a
backtracking parser since it will eventually error and then know to
pick the other rule. However, I really think this is a bad idea.So how can we have shorter closures without this mess? One simple way
is to require thefunction
prefix:// clearly an array with an anonymous function [function($x) => $x * 2]; // clearly yields an anonymous function yield function($x) => $x * 2; // clearly an anonymous function function(Type &$x) => expr;
Requiring the
function
prefix mitigates one of the value parts of
arrow functions: they are short.Another option would be to resolve the ambiguities with keys and
values but to change the type information in parameters:(&$input: array) => expr
By putting the type after the variable (similar to how we declare
return types) we no longer have the issues with mis-parsing. Of
course, that's not how we declare parameter types currently. I think
we would need to permit it everywhere and deprecate the current syntax
with the type being prefixed. (By deprecate I mean in PHP 8 and not
remove it until PHP 9 or later)I would prefer that we shorten the
function
keyword tofn
:[fn($x) => $x * 2]
This preserves the shortness of the expression while providing
unambiguous, simple parsing. Of course, now we have a similar issue:
we have bothfn
andfunction
.What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.--
Hello,
personally, as a purely userland dev, I feel that the correct syntax
would be the "simplest" one - and that's imho the (Type &$x) ==> exprOr the version with ~> operator - I don't care which one gets in,
althought I would rather see the ==> one. :)I understand that there might be some ambiguity in the parser that
would need to be solved by backtracking, but for using the language,
the "simplest" option should IMHO be the correct one. Also not
introducing any more "stuff" (like new parameter types syntax) would
be a plus.For the need to have a single parameter enclosed with ( ) - by
thinking more and more about it, I think that having the one special
case for not requiring parenthesis around parameters is pretty uselss,
since it would have to be there anyways if you wrote the typehint.PS: the [fn($x) => $x * 2] seems ambigous, from reader's POV; key of
the item is result of fn($x) and value is $x * 2? Also, it would be a
huge BC break with not allowing you to name functions fn(), wouldn't
it?--
Regards
Pavel Kouřil
Being a userland developer myself I totally agree! Keeping it simple,
consistent & unambiguous.
Regards //Björn Larsson
Am 26.9.2015 um 18:17 schrieb Levi Morrison levim@php.net:
(Email in gist format:
https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c)In EcmaScript 2015 (ES6) the expression
(x) => x * 2
means to create
an anonymous function with one parameterx
that will returnx * 2
.
For example:(x) => x * 2
// is equivalent to:
function(x) { return x * 2; }A modified example from documentation by Mozilla Developer
Network page demonstrates how they are useful:var a = [
"Hydrogen",
"Helium",
"Lithium",
"Beryllium"
];var a2 = a.map(function(s){ return s.length }); // pre-ES6
var a3 = a.map((s) => s.length); // ES6
There has been some talk about how we can use arrow function
expressions in PHP. In PHP using the same syntax would have some
ambiguities:// Does this mean:
// 1. Create an array key with the result of($x)
and a value
with$x * 2
// 2. Create an array with one value that is an anonymous function
[($x) => $x * 2]// Does this mean:
// 1. Yield a key with the result of($x)
and a value with$x * 2
// 2. Yield an anonymous function
yield ($x) => $x * 2;This is why Bob Weinand proposed using
~>
instead of=>
.
However, if we allow type declarations there is another issue. In the
definition(Type &$x) => expr
the(Type &$var)
part can parse as
"take constantType
and variable$var
and do a bitwise and&
operation." After that the=>
will be an unexpected token. Even
though the rule would be invalid the parser doesn't know that far
ahead it will error and it doesn't know which rule to pick. Changing
the token from=>
to~>
doesn't affect this issue.We could solve the first ambiguities with prefering the current
meaning withkey => value
and requiring the meaning with closures to
wrap them in()
. We could solve the latter ambiguity with a
backtracking parser since it will eventually error and then know to
pick the other rule. However, I really think this is a bad idea.So how can we have shorter closures without this mess? One simple way
is to require thefunction
prefix:// clearly an array with an anonymous function
[function($x) => $x * 2];// clearly yields an anonymous function
yield function($x) => $x * 2;// clearly an anonymous function
function(Type &$x) => expr;Requiring the
function
prefix mitigates one of the value parts of
arrow functions: they are short.Another option would be to resolve the ambiguities with keys and
values but to change the type information in parameters:(&$input: array) => expr
By putting the type after the variable (similar to how we declare
return types) we no longer have the issues with mis-parsing. Of
course, that's not how we declare parameter types currently. I think
we would need to permit it everywhere and deprecate the current syntax
with the type being prefixed. (By deprecate I mean in PHP 8 and not
remove it until PHP 9 or later)I would prefer that we shorten the
function
keyword tofn
:fn($x) => $x * 2
This preserves the shortness of the expression while providing
unambiguous, simple parsing. Of course, now we have a similar issue:
we have bothfn
andfunction
.What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.
Hey,
Thanks a lot for your thoughts on it.
I think unlike a lot of critics in the original thread, this actually is a viable solution.
As I mentioned, I’d prefer fn($x) => $x ** 2 as general short Closure syntax (no alias to function), for better distinguishing from the normal Closures.
Alternatively, Objective-C has also an interesting syntax with a leading ^ (instead of your function).
But, as long as we want types etc, we definitely need a prefix (as long as we don’t want ugly parser hacking) … And in general, the prefix also provides better recognizability that it actually should be a short Closure [Also a reason why I dislike reusing function as prefix here].
May you please set up a formal RFC for that in the wiki so that I may withdraw my current RFC in favor of yours, which, I think, honestly is a superior solution as it addresses types and as said, might be easier to recognize at a quick glance.
[And also obsoletes a lot of complaints about the operator … Seems more people than expected actually dislike ~> ...]
Thanks,
Bob
(Email in gist format:
https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c)In EcmaScript 2015 (ES6) the expression
(x) => x * 2
means to create
an anonymous function with one parameterx
that will returnx * 2
.
For example:(x) => x * 2 // is equivalent to: function(x) { return x * 2; }
A modified example from documentation by Mozilla Developer
Network page demonstrates how they are useful:var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(function(s){ return s.length }); // pre-ES6 var a3 = a.map((s) => s.length); // ES6
There has been some talk about how we can use arrow function
expressions in PHP. In PHP using the same syntax would have some
ambiguities:// Does this mean: // 1. Create an array key with the result of `($x)` and a value
with
$x * 2
// 2. Create an array with one value that is an anonymous function
[($x) => $x * 2]// Does this mean: // 1. Yield a key with the result of `($x)` and a value with `$x * 2` // 2. Yield an anonymous function yield ($x) => $x * 2;
This is why Bob Weinand proposed using
~>
instead of=>
.
However, if we allow type declarations there is another issue. In the
definition(Type &$x) => expr
the(Type &$var)
part can parse as
"take constantType
and variable$var
and do a bitwise and&
operation." After that the=>
will be an unexpected token. Even
though the rule would be invalid the parser doesn't know that far
ahead it will error and it doesn't know which rule to pick. Changing
the token from=>
to~>
doesn't affect this issue.We could solve the first ambiguities with prefering the current
meaning withkey => value
and requiring the meaning with closures to
wrap them in()
. We could solve the latter ambiguity with a
backtracking parser since it will eventually error and then know to
pick the other rule. However, I really think this is a bad idea.So how can we have shorter closures without this mess? One simple way
is to require thefunction
prefix:// clearly an array with an anonymous function [function($x) => $x * 2]; // clearly yields an anonymous function yield function($x) => $x * 2; // clearly an anonymous function function(Type &$x) => expr;
Requiring the
function
prefix mitigates one of the value parts of
arrow functions: they are short.Another option would be to resolve the ambiguities with keys and
values but to change the type information in parameters:(&$input: array) => expr
By putting the type after the variable (similar to how we declare
return types) we no longer have the issues with mis-parsing. Of
course, that's not how we declare parameter types currently. I think
we would need to permit it everywhere and deprecate the current syntax
with the type being prefixed. (By deprecate I mean in PHP 8 and not
remove it until PHP 9 or later)I would prefer that we shorten the
function
keyword tofn
:[fn($x) => $x * 2]
This preserves the shortness of the expression while providing
unambiguous, simple parsing. Of course, now we have a similar issue:
we have bothfn
andfunction
.What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.
If my opinion is worth anything, I actually like how fn($x) => $x * 2
looks the most. It's fairly short like the original proposal, but has
the advantage of clearly appearing to be a function. That was a large
complaint on the whole "short closures" idea in the first place, and PHP
usually does a good job at making code very obvious and clear.
So yeah, an "fn" prefix (and requiring parenthesis always) looks very
consistent, but still is short.
I would prefer that we shorten the
function
keyword tofn
:
Do you mean generally, or just in short closures? Turning the keyword
everywhere would be a huge BC break (though pretty easy to fix in code:
"s/function\s/fn /g" :-) ). I'd be OK with allowing both everywhere for
consistency though:
fn square(int $x) {
return $x * $x;
}
$squaresPlusOne = array_map(function(int $x) => square($x) + 1, [1,
2, 3, 4]);
class Foo {
public fn __construct() {}
}
You get the idea...
I actually really like that + your idea. Kudos.
--
Stephen
(Email in gist format:
https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c)In EcmaScript 2015 (ES6) the expression
(x) => x * 2
means to create
an anonymous function with one parameterx
that will returnx * 2
.
For example:(x) => x * 2 // is equivalent to: function(x) { return x * 2; }
A modified example from documentation by Mozilla Developer
Network page demonstrates how they are useful:var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(function(s){ return s.length }); // pre-ES6 var a3 = a.map((s) => s.length); // ES6
There has been some talk about how we can use arrow function
expressions in PHP. In PHP using the same syntax would have some
ambiguities:// Does this mean: // 1. Create an array key with the result of `($x)` and a value
with
$x * 2
// 2. Create an array with one value that is an anonymous function
[($x) => $x * 2]// Does this mean: // 1. Yield a key with the result of `($x)` and a value with `$x *
2`
// 2. Yield an anonymous function
yield ($x) => $x * 2;This is why Bob Weinand proposed using
~>
instead of=>
.
However, if we allow type declarations there is another issue. In the
definition(Type &$x) => expr
the(Type &$var)
part can parse as
"take constantType
and variable$var
and do a bitwise and&
operation." After that the=>
will be an unexpected token. Even
though the rule would be invalid the parser doesn't know that far
ahead it will error and it doesn't know which rule to pick. Changing
the token from=>
to~>
doesn't affect this issue.We could solve the first ambiguities with prefering the current
meaning withkey => value
and requiring the meaning with closures to
wrap them in()
. We could solve the latter ambiguity with a
backtracking parser since it will eventually error and then know to
pick the other rule. However, I really think this is a bad idea.So how can we have shorter closures without this mess? One simple way
is to require thefunction
prefix:// clearly an array with an anonymous function [function($x) => $x * 2]; // clearly yields an anonymous function yield function($x) => $x * 2; // clearly an anonymous function function(Type &$x) => expr;
Requiring the
function
prefix mitigates one of the value parts of
arrow functions: they are short.Another option would be to resolve the ambiguities with keys and
values but to change the type information in parameters:(&$input: array) => expr
By putting the type after the variable (similar to how we declare
return types) we no longer have the issues with mis-parsing. Of
course, that's not how we declare parameter types currently. I think
we would need to permit it everywhere and deprecate the current syntax
with the type being prefixed. (By deprecate I mean in PHP 8 and not
remove it until PHP 9 or later)I would prefer that we shorten the
function
keyword tofn
:[fn($x) => $x * 2]
This preserves the shortness of the expression while providing
unambiguous, simple parsing. Of course, now we have a similar issue:
we have bothfn
andfunction
.What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.If my opinion is worth anything, I actually like how fn($x) => $x * 2 looks
the most. It's fairly short like the original proposal, but has the
advantage of clearly appearing to be a function. That was a large
complaint on the whole "short closures" idea in the first place, and PHP
usually does a good job at making code very obvious and clear.So yeah, an "fn" prefix (and requiring parenthesis always) looks very
consistent, but still is short.I would prefer that we shorten the
function
keyword tofn
:Do you mean generally, or just in short closures? Turning the keyword
everywhere would be a huge BC break (though pretty easy to fix in code:
"s/function\s/fn /g" :-) ). I'd be OK with allowing both everywhere for
consistency though:fn square(int $x) { return $x * $x; } $squaresPlusOne = array_map(function(int $x) => square($x) + 1, [1, 2,
3, 4]);
class Foo { public fn __construct() {} }
You get the idea...
I actually really like that + your idea. Kudos.
I am definitely not proposing to remove function
at this time. That
would be a huge BC break, indeed! I meant only that fn
can be used
for brevity if that is preferred and it could be used in other
places as well. This is my preference, but I know other people don't
like that, which is why I would intend to keep it as a separate vote
from the arrow part (=> expr).
(Email in gist format:
https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c)In EcmaScript 2015 (ES6) the expression
(x) => x * 2
means to create
an anonymous function with one parameterx
that will returnx * 2
.
For example:(x) => x * 2 // is equivalent to: function(x) { return x * 2; }
A modified example from [documentation by Mozilla Developer
Network][1] page demonstrates how they are useful:var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(function(s){ return s.length }); // pre-ES6 var a3 = a.map((s) => s.length); // ES6
There has been some talk about how we can use arrow function
expressions in PHP. In PHP using the same syntax would have some
ambiguities:// Does this mean: // 1. Create an array key with the result of `($x)` and a value
with
$x * 2
// 2. Create an array with one value that is an anonymous function
[($x) => $x * 2]// Does this mean: // 1. Yield a key with the result of `($x)` and a value with `$x *
2`
// 2. Yield an anonymous function
yield ($x) => $x * 2;This is why Bob Weinand [proposed][2] using
~>
instead of=>
.
However, if we allow type declarations there is another issue. In the
definition(Type &$x) => expr
the(Type &$var)
part can parse as
"take constantType
and variable$var
and do a bitwise and&
operation." After that the=>
will be an unexpected token. Even
though the rule would be invalid the parser doesn't know that far
ahead it will error and it doesn't know which rule to pick. Changing
the token from=>
to~>
doesn't affect this issue.We could solve the first ambiguities with prefering the current
meaning withkey => value
and requiring the meaning with closures to
wrap them in()
. We could solve the latter ambiguity with a
backtracking parser since it will eventually error and then know to
pick the other rule. However, I really think this is a bad idea.So how can we have shorter closures without this mess? One simple way
is to require thefunction
prefix:// clearly an array with an anonymous function [function($x) => $x * 2]; // clearly yields an anonymous function yield function($x) => $x * 2; // clearly an anonymous function function(Type &$x) => expr;
Requiring the
function
prefix mitigates one of the value parts of
arrow functions: they are short.Another option would be to resolve the ambiguities with keys and
values but to change the type information in parameters:(&$input: array) => expr
By putting the type after the variable (similar to how we declare
return types) we no longer have the issues with mis-parsing. Of
course, that's not how we declare parameter types currently. I think
we would need to permit it everywhere and deprecate the current syntax
with the type being prefixed. (By deprecate I mean in PHP 8 and not
remove it until PHP 9 or later)I would prefer that we shorten the
function
keyword tofn
:[fn($x) => $x * 2]
This preserves the shortness of the expression while providing
unambiguous, simple parsing. Of course, now we have a similar issue:
we have bothfn
andfunction
.What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.[1]:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
[2]: https://wiki.php.net/rfc/short_closuresIf my opinion is worth anything, I actually like how fn($x) => $x * 2 looks
the most. It's fairly short like the original proposal, but has the
advantage of clearly appearing to be a function. That was a large
complaint on the whole "short closures" idea in the first place, and PHP
usually does a good job at making code very obvious and clear.So yeah, an "fn" prefix (and requiring parenthesis always) looks very
consistent, but still is short.I would prefer that we shorten the
function
keyword tofn
:Do you mean generally, or just in short closures? Turning the keyword
everywhere would be a huge BC break (though pretty easy to fix in code:
"s/function\s/fn /g" :-) ). I'd be OK with allowing both everywhere for
consistency though:fn square(int $x) { return $x * $x; } $squaresPlusOne = array_map(function(int $x) => square($x) + 1, [1, 2,
3, 4]);
class Foo { public fn __construct() {} }
You get the idea...
I actually really like that + your idea. Kudos.
I am definitely not proposing to remove
function
at this time. That
would be a huge BC break, indeed! I meant only thatfn
can be used
for brevity if that is preferred and it could be used in other
places as well. This is my preference, but I know other people don't
like that, which is why I would intend to keep it as a separate vote
from the arrow part (=> expr).
That sounds like a good plan. At this time then, a
$square = function(int $x) => $x * $x;
syntax with auto variable capture is what I'm currently hoping for. :)
--
Stephen
> On Wed, Sep 30, 2015 at 11:59 PM, Stephen Coakley
>>
>>>
>>> (Email in gist format:
>>> https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c)
>>>
>>> In EcmaScript 2015 (ES6) the expression `(x) => x * 2` means to create
>>> an anonymous function with one parameter `x` that will return `x * 2`.
>>> For example:
>>>
>>> (x) => x * 2
>>> // is equivalent to:
>>> function(x) { return x * 2; }
>>>
>>> A modified example from [documentation by Mozilla Developer
>>> Network][1] page demonstrates how they are useful:
>>>
>>> var a = [
>>> "Hydrogen",
>>> "Helium",
>>> "Lithium",
>>> "Beryllium"
>>> ];
>>>
>>> var a2 = a.map(function(s){ return s.length }); // pre-ES6
>>>
>>> var a3 = a.map((s) => s.length); // ES6
>>>
>>> There has been some talk about how we can use arrow function
>>> expressions in PHP. In PHP using the same syntax would have some
>>> ambiguities:
>>>
>>> // Does this mean:
>>> // 1. Create an array key with the result of `($x)` and a value
>>> with `$x * 2`
>>> // 2. Create an array with one value that is an anonymous
function
>>> [($x) => $x * 2]
>>>
>>> // Does this mean:
>>> // 1. Yield a key with the result of `($x)` and a value
with `$x *
>>> 2`
>>> // 2. Yield an anonymous function
>>> yield ($x) => $x * 2;
>>>
>>> This is why Bob Weinand [proposed][2] using `~>` instead of `=>`.
>>> However, if we allow type declarations there is another issue. In the
>>> definition `(Type &$x) => expr` the `(Type &$var)` part can parse as
>>> "take constant `Type` and variable `$var` and do a bitwise and `&`
>>> operation." After that the `=>` will be an unexpected token. Even
>>> though the rule would be invalid the parser doesn't know that far
>>> ahead it will error and it doesn't know which rule to pick. Changing
>>> the token from `=>` to `~>` doesn't affect this issue.
>>>
>>> We could solve the first ambiguities with prefering the current
>>> meaning with `key => value` and requiring the meaning with closures to
>>> wrap them in `()`. We could solve the latter ambiguity with a
>>> backtracking parser since it will eventually error and then know to
>>> pick the other rule. However, I really think this is a bad idea.
>>>
>>> So how can we have shorter closures without this mess? One simple way
>>> is to require the `function` prefix:
>>>
>>> // clearly an array with an anonymous function
>>> [function($x) => $x * 2];
>>>
>>> // clearly yields an anonymous function
>>> yield function($x) => $x * 2;
>>>
>>> // clearly an anonymous function
>>> function(Type &$x) => expr;
>>>
>>> Requiring the `function` prefix mitigates one of the value parts of
>>> arrow functions: they are short.
>>>
>>> Another option would be to resolve the ambiguities with keys and
>>> values but to change the type information in parameters:
>>>
>>> (&$input: array) => expr
>>>
>>> By putting the type after the variable (similar to how we declare
>>> return types) we no longer have the issues with mis-parsing. Of
>>> course, that's not how we declare parameter types currently. I think
>>> we would need to permit it everywhere and deprecate the current syntax
>>> with the type being prefixed. (By deprecate I mean in PHP 8 and not
>>> remove it until PHP 9 or later)
>>>
>>> I would prefer that we shorten the `function` keyword to `fn`:
>>>
>>> [fn($x) => $x * 2]
>>>
>>> This preserves the shortness of the expression while providing
>>> unambiguous, simple parsing. Of course, now we have a similar issue:
>>> we have both `fn` and `function`.
>>>
>>> What concerns do you have about `fn($x) => $x * 2` or `function($x) =>
>>> $x * 2`? I will be writing a proper RFC later but I wanted to get
>>> discussion going now.
>>>
>>> [1]:
>>>
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
>>> [2]: https://wiki.php.net/rfc/short_closures
>>>
>>
>> If my opinion is worth anything, I actually like how fn($x) => $x *
2 looks
>> the most. It's fairly short like the original proposal, but has the
>> advantage of *clearly* appearing to be a function. That was a large
>> complaint on the whole "short closures" idea in the first place, and PHP
>> usually does a good job at making code very obvious and clear.
>>
>> So yeah, an "fn" prefix (and requiring parenthesis always) looks very
>> consistent, but still is short.
>>
>>> I would prefer that we shorten the `function` keyword to `fn`:
>>
>> Do you mean generally, or just in short closures? Turning the keyword
>> everywhere would be a huge BC break (though pretty easy to fix in code:
>> "s/function\s/fn /g" :-) ). I'd be OK with allowing both everywhere for
>> consistency though:
>>
>> fn square(int $x) {
>> return $x * $x;
>> }
>>
>> $squaresPlusOne = array_map(function(int $x) => square($x) + 1,
[1, 2,
>> 3, 4]);
>>
>> class Foo {
>> public fn __construct() {}
>> }
>>
>> You get the idea...
>>
>> I actually really like that + your idea. Kudos.
>
> I am definitely not proposing to remove `function` at this time. That
> would be a huge BC break, indeed! I meant only that `fn` can be used
> for brevity if that is preferred and it *could* be used in other
> places as well. This is my preference, but I know other people don't
> like that, which is why I would intend to keep it as a separate vote
> from the arrow part (=> expr).
>
That sounds like a good plan. At this time then, a
$square = function(int $x) => $x * $x;
syntax with auto variable capture is what I'm currently hoping for. :)
--
Stephen
Hi,
Given the recent discussion about async/await keyword should one
also consider short closures supporting asynchronous functionality
in the future?
Stumbled upon it when reading about Async Lambdas in Hacklang
manual.
Regards //Björn Larsson
Den 2015-09-26 kl. 18:17, skrev Levi Morrison:
(Email in gist format:
https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c)In EcmaScript 2015 (ES6) the expression
(x) => x * 2
means to create
an anonymous function with one parameterx
that will returnx * 2
.
For example:(x) => x * 2 // is equivalent to: function(x) { return x * 2; }
A modified example from documentation by Mozilla Developer
Network page demonstrates how they are useful:var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(function(s){ return s.length }); // pre-ES6 var a3 = a.map((s) => s.length); // ES6
There has been some talk about how we can use arrow function
expressions in PHP. In PHP using the same syntax would have some
ambiguities:// Does this mean: // 1. Create an array key with the result of `($x)` and a value
with
$x * 2
// 2. Create an array with one value that is an anonymous function
[($x) => $x * 2]// Does this mean: // 1. Yield a key with the result of `($x)` and a value with `$x * 2` // 2. Yield an anonymous function yield ($x) => $x * 2;
This is why Bob Weinand proposed using
~>
instead of=>
.
However, if we allow type declarations there is another issue. In the
definition(Type &$x) => expr
the(Type &$var)
part can parse as
"take constantType
and variable$var
and do a bitwise and&
operation." After that the=>
will be an unexpected token. Even
though the rule would be invalid the parser doesn't know that far
ahead it will error and it doesn't know which rule to pick. Changing
the token from=>
to~>
doesn't affect this issue.We could solve the first ambiguities with prefering the current
meaning withkey => value
and requiring the meaning with closures to
wrap them in()
. We could solve the latter ambiguity with a
backtracking parser since it will eventually error and then know to
pick the other rule. However, I really think this is a bad idea.So how can we have shorter closures without this mess? One simple way
is to require thefunction
prefix:// clearly an array with an anonymous function [function($x) => $x * 2]; // clearly yields an anonymous function yield function($x) => $x * 2; // clearly an anonymous function function(Type &$x) => expr;
Requiring the
function
prefix mitigates one of the value parts of
arrow functions: they are short.Another option would be to resolve the ambiguities with keys and
values but to change the type information in parameters:(&$input: array) => expr
By putting the type after the variable (similar to how we declare
return types) we no longer have the issues with mis-parsing. Of
course, that's not how we declare parameter types currently. I think
we would need to permit it everywhere and deprecate the current syntax
with the type being prefixed. (By deprecate I mean in PHP 8 and not
remove it until PHP 9 or later)I would prefer that we shorten the
function
keyword tofn
:[fn($x) => $x * 2]
This preserves the shortness of the expression while providing
unambiguous, simple parsing. Of course, now we have a similar issue:
we have bothfn
andfunction
.What concerns do you have about
fn($x) => $x * 2
orfunction($x) => $x * 2
? I will be writing a proper RFC later but I wanted to get
discussion going now.