rfc:short_closures
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
rfc:short_closures [2015/05/01 01:44] – created bwoebi | rfc:short_closures [2017/09/22 13:28] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Short Closures ====== | ====== PHP RFC: Short Closures ====== | ||
- | * Version: 0.1 | + | * Version: 0.2 |
* Date: 2015-05-01 | * Date: 2015-05-01 | ||
* Author: Bob Weinand, bobwei9@hotmail.com | * Author: Bob Weinand, bobwei9@hotmail.com | ||
- | * Status: | + | * Status: |
* First Published at: http:// | * First Published at: http:// | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | When writing partial | + | Anonymous |
+ | |||
+ | The current implementation of anonymous functions in PHP is quite verbose compared | ||
+ | |||
+ | A better syntax encourages functional code and partial applications | ||
===== Proposal ===== | ===== Proposal ===== | ||
- | This RFC proposes the introduction of the ~> operator. | + | This RFC proposes the introduction of the ~> operator |
- | The ~> operator defines a shorthand Closure which automatically use()'s all the used compiled variables | + | Current code: |
+ | <code php> | ||
+ | function ($x) { | ||
+ | return $x * 2; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | would be equivalent to the new syntax: | ||
+ | <code php> | ||
+ | $x ~> $x * 2 | ||
+ | </ | ||
+ | |||
+ | Anonymous functions defined in this way will automatically | ||
+ | |||
+ | ==== Syntax ==== | ||
+ | The syntax used to define a short hand anonymous function would be: | ||
+ | |||
+ | * Parameters. When the function has a single parameter the surrounding parentheses (aka round brackets) may be omitted. For functions with multiple parameters the parentheses are required. | ||
+ | * The new short closure operator ~> | ||
+ | * The body of the anonymous function. When the body of the function is a single expression the surrounding curly brackets and return keyword may be omitted. When the body of the function is not a single expression, the braces | ||
+ | |||
+ | I.e. all of the following would be equivalent: | ||
+ | <code php> | ||
+ | $x ~> $x * 2 | ||
+ | $x ~> { return $x * 2;} | ||
+ | ($x) ~> $x * 2 | ||
+ | ($x) ~> { return $x * 2; } | ||
+ | </ | ||
+ | |||
+ | Omitting the parentheses when the function has multiple parameters will result in a parse error: | ||
+ | <code php> | ||
+ | $x, $y ~> {$x + $y} // Unexpected ',' | ||
+ | ($x, $y) ~> $x + $y // correct | ||
+ | </ | ||
+ | |||
+ | Using the return keyword when braces have been omitted, will similarly give a parse error: | ||
+ | <code php> | ||
+ | ($x, $y) ~> return $x + $y; // Unexpected T_RETURN | ||
+ | ($x, $y) ~> { return $x + $y; } // correct | ||
+ | </ | ||
+ | |||
+ | In case of no parameters, an empty parenthesis pair is needed. | ||
+ | <code php> | ||
+ | ~> 2 * 3; // Unexpected T_TILDED_ARROW | ||
+ | () ~> 2 * 3; // correct, will return 6 when called | ||
+ | </ | ||
- | Concrete syntax is (~> is right associative with highest | + | Concrete syntax is (~> is right associative with lowest |
< | < | ||
( parameter_list ) ~> expression | ( parameter_list ) ~> expression | ||
Line 25: | Line 74: | ||
| $variable ~> { statements } | | $variable ~> { statements } | ||
</ | </ | ||
+ | |||
When a bare expression is used as second parameter, its result will be the return value of the Closure. | When a bare expression is used as second parameter, its result will be the return value of the Closure. | ||
- | ===== Examples ===== | + | Also, parameter_list does //not// include default values nor type hints. See also the 'Type Hints and Return Types' section at the bottom. |
- | ==== Partial application | + | |
+ | // | ||
+ | This RFC stance is that chained short Closures followed by a full Closure would look quite weird: '' | ||
+ | |||
+ | // | ||
+ | While it might appear not consistent, with any other number of parameters, a lot of languages having extra short Closures allow this. Also, Closures with just one parameter are relatively common, so this RFC author thinks it is worth supporting that. | ||
+ | |||
+ | ==== Variable binding | ||
+ | The position of this RFC is that the shorthand syntax is to allow anonymous functions to be used as easily as possible. Therefore, rather than requiring individual variables be bound to the closure through the '' | ||
+ | |||
+ | The variable binding is always **by value**. There are no implicit references. If these are needed, the current syntax with '' | ||
+ | |||
+ | For example: | ||
<code php> | <code php> | ||
- | function | + | $a = 1; |
- | return $left ~> $right ~> $left + $right; | + | function |
+ | $c = rand(0, 4); | ||
+ | |||
+ | return | ||
} | } | ||
+ | </ | ||
- | /* Just to compare | + | Variables $b and $c would be bound automatically |
- | function | + | <code php> |
- | | + | $a = 1; |
- | return | + | function |
- | return | + | // Notice: Undefined variable: a in %s on line %d |
- | } | + | |
- | } | + | |
} | } | ||
</ | </ | ||
+ | |||
+ | If a user wants to avoid binding all variables automatically they can use the current syntax to define the anonymous function. | ||
+ | |||
+ | ===== Examples ===== | ||
+ | These examples cover some simple operations and show how the short-hand syntax is easier to read compared to the existing long-hand syntax. | ||
+ | |||
+ | ==== Array sort with user function ==== | ||
+ | Sort '' | ||
+ | |||
+ | Current syntax: | ||
<code php> | <code php> | ||
- | /* Thanks to Levi Morrison for that example */ | + | usort($array, |
- | function | + | function($a, $b) { |
- | return $initial ~> $input ~> { | + | return $a->val <=> $b->val; |
- | $accumulator | + | } |
- | | + | ); |
- | $accumulator | + | </ |
+ | |||
+ | New syntax: | ||
+ | <code php> | ||
+ | usort($array, ($a, $b) ~> $a->val <=> $b->val); | ||
+ | </ | ||
+ | |||
+ | ==== Extracting data from an array and summing it ==== | ||
+ | Current syntax: | ||
+ | <code php> | ||
+ | function sumEventScores($events, | ||
+ | $types = array_map( | ||
+ | function($event) { | ||
+ | return $event[' | ||
+ | }, | ||
+ | $events | ||
+ | ); | ||
+ | |||
+ | return array_reduce( | ||
+ | | ||
+ | function($sum, $type) use ($scores) { | ||
+ | return $sum + $scores[$type]; | ||
} | } | ||
- | return $accumulator; | + | ); |
- | }; | + | |
} | } | ||
+ | </ | ||
- | /* Compared | + | New syntax: |
+ | <code php> | ||
+ | function sumEventScores($events, | ||
+ | $types = array_map($event ~> $event[' | ||
+ | return array_reduce($types, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The calling code for this function would be: | ||
+ | |||
+ | <code php> | ||
+ | $events = array( | ||
+ | array( | ||
+ | ' | ||
+ | ' | ||
+ | ), | ||
+ | array( | ||
+ | ' | ||
+ | ' | ||
+ | ), | ||
+ | //... | ||
+ | ); | ||
+ | |||
+ | $scores = [ | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ]; | ||
+ | |||
+ | sumEventScores($events, | ||
+ | </ | ||
+ | |||
+ | ==== Lazy evaluation ==== | ||
+ | It may be necessary to have code only evaluated under specific conditions, like debugging code: | ||
+ | <code php> | ||
+ | function runDebug(callable $func) { | ||
+ | | ||
+ | if (DEBUG) { | ||
+ | try { | ||
+ | $func(); | ||
+ | } catch (Exception $e) { /*... */ } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $myFile = "/ | ||
+ | |||
+ | /* Old code */ | ||
+ | runDebug(function() use ($myFile) { /* yeah, we have to use use ($myFile) here, which isn't really helpful in this context | ||
+ | if (!file_exists($myFile)) { | ||
+ | throw new Exception(" | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | /* New code */ | ||
+ | runDebug(() ~> { | ||
+ | if (!file_exists($myFile)) { | ||
+ | throw new Exception(" | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | /* still continue here, unlike an assert which would unwind the stack frame here ... */ | ||
+ | </ | ||
+ | |||
+ | ==== Partial application ==== | ||
+ | The shorthand syntax makes it easier to write functional code like a reducer by using the ability of shorthand anonymous functions to be chained together easily. | ||
+ | |||
+ | Current syntax: | ||
+ | <code php> | ||
function reduce(callable $fn) { | function reduce(callable $fn) { | ||
return function($initial) use ($fn) { | return function($initial) use ($fn) { | ||
- | return function ($input) use ($fn, $initial) { | + | return function($input) use ($fn, $initial) { |
$accumulator = $initial; | $accumulator = $initial; | ||
foreach ($input as $value) { | foreach ($input as $value) { | ||
Line 69: | Line 233: | ||
</ | </ | ||
- | ==== Quick applications to arrays ==== | + | New syntax: |
<code php> | <code php> | ||
- | /* Get a range from 2 to 10 with increment 2 */ | + | function reduce(callable $fn) { |
- | $array = array_map($x ~> $x * 2, range(1, 5)); | + | |
- | + | | |
- | /* Compared to */ | + | foreach ($input |
- | foreach (range(1, 5) as $x) { | + | $accumulator |
- | $array[] | + | } |
+ | return $accumulator; | ||
+ | }; | ||
} | } | ||
- | /* or */ | ||
- | $array = array_map(function($x) { return $x * 2; }, range(1, 5)); | ||
</ | </ | ||
- | <code php> | + | ===== Symbol choice ===== |
- | /* Let $array be an array filled with objects having property val. Sort them in reverse by that property. */ | + | |
- | usort($array, | + | |
- | /* Compared | + | The symbol '' |
- | usort($array, | + | |
- | /* ... which will be probably multilined | + | |
- | usort($array, | + | |
- | return -($a-> | + | |
- | }); | + | |
- | </ | + | |
- | ===== General thoughts ===== | + | Currently Hack has implemented shorthand anonymous functions using the '' |
- | ==== Do we really need this? ==== | + | |
- | Ultimately, it does not add any additional possibilities which weren' | + | |
- | But it prevents us from having to write too much boilerplate, | + | This returns an array containing an anonymous function: |
+ | <code php> | ||
+ | return [$x ==> | ||
+ | </code> | ||
- | Aside from that, it encourages (or makes it at least much tedious task) functional code and partial applications (see the examples), which may be a powerful tool the language shouldn' | + | This returns an array if $x is already |
+ | <code php> | ||
+ | return [$x => $x * 2]; | ||
+ | </ | ||
- | ==== Why ~> ? ==== | + | Additionally, |
- | Hack has %%==>%%, so, why then ~>? | + | |
- | ~> has the advantage | + | Also, Hack has some possibilities |
- | + | ||
- | That's why this RFC proposes | + | |
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
Line 116: | Line 273: | ||
===== Future Scope ===== | ===== Future Scope ===== | ||
- | This RFC _does not_ propose | + | ==== Other uses for ~> operator ==== |
+ | This RFC is solely for using the shorthand | ||
<code php> | <code php> | ||
class Foo { | class Foo { | ||
private $bar: | private $bar: | ||
- | getBar ~> $this-> | + | getBar() ~> $this-> |
setBar($bar) ~> $this-> | setBar($bar) ~> $this-> | ||
} | } | ||
</ | </ | ||
- | ===== Proposed Voting Choices | + | Which is outside the scope of this RFC. |
+ | |||
+ | ==== Type Hints and Return Types ==== | ||
+ | This RFC does //not// include type hints nor return types. | ||
+ | |||
+ | Type Hints are not added due to technical problems in parser and the RFC author is not sure about whether they should be really added. If anyone achieves to solve these technical issues, he should feel free to do that in a future RFC for further discussion. | ||
+ | And as introducing half a typesystem would be inconsistent, | ||
+ | |||
+ | As an alternative, | ||
+ | |||
+ | ===== Vote ===== | ||
This RFC is a language change and as such needs a 2/3 majority. | This RFC is a language change and as such needs a 2/3 majority. | ||
- | ===== Patches | + | Voting opened September 22th, 2015 and will remain open until October 2nd, 2015. |
- | Pull request is at https:// | + | |
- | ===== | + | <doodle title="Short Closures" |
+ | * Yes | ||
+ | * No | ||
+ | </ | ||
+ | |||
+ | ===== Patch ===== | ||
+ | Pull request is at https:// |
rfc/short_closures.1430444679.txt.gz · Last modified: 2017/09/22 13:28 (external edit)