====== PHP RFC: Function Referencing as Closures ======
* Version: 0.1
* Date: 2014-08-03, latest 2014-08-18, withdrawn 2014-11-03
* Author: Andrea Faulds, ajf@ajf.me
* Status: Withdrawn
* First Published at: http://wiki.php.net/rfc/function_referencing
===== Introduction =====
For the longest time, PHP has lacked [[https://en.wikipedia.org/wiki/First-class_function|first-class functions]]. Because of the separate namespaces, functions and methods can't be dealt with directly like other arguments, and you must refer to them with strings or arrays. This means that references to functions are not necessarily obvious, as they simply look like a normal string or array, and cannot be validated until call-time or passed to a function with a ''callable'' typehint. This can make code harder to read unless you are familiar with a function's signature, for both humans and machines.
PHP 5.3 added anonymous functions (or closures), which support nesting, can close over both variable and class scopes, and can be arbitrarily scoped, bound and applied at runtime. Aside from not directly supporting partial application, PHP's closures are first-class functions. However, they are segregated from standard functions and methods, which do not enjoy the benefits closures receive.
This RFC proposes a new syntax for referencing normal functions and methods as closures, bringing the benefits of first-class functions to them, making references to functions obvious, and allowing immediate validation that functions being referenced exist.
===== Proposal =====
==== Overview ====
A function can be referenced with the ''&'' operator, returning a closure:
$func = &strlen;
array_map($func, ['foo', 'foobar', 'elePHPant']); // [3, 6, 9];
As it is an ordinary expression, it can be used inline:
array_map(&strlen, ['foo', 'foobar', 'elePHPant']); // [3, 6, 9];
This works also for static methods and methods (here we use the ''->call'' method for convenience, which [[rfc:closure_apply|this RFC]] would add):
class FooBar {
private $x;
public function __construct($x) { $this->x = $x; }
public function get() { return $this->x; }
public static function getStatic(FooBar $obj) { return $obj->x; }
}
$qux = new FooBar(3);
// Static functions referenced retain their scope
$func = &FooBar::getStatic;
// Thus it can see the instance variables of $qux
$func($qux); // 3
$func = &FooBar::get;
$func->call($qux); // 3
// Or, if we wish to bind
$func = Closure::bind(&FooBar::get, $qux);
==== Details ====
The current ''&'' syntax used for referencing variables is extended to support functions:
function_reference:
'&' namespace_name
| '&' T_NAMESPACE T_NS_SEPARATOR namespace_name
| '&' T_NS_SEPARATOR namespace_name
| '&' class_name T_PAAMAYIM_NEKUDOTAYIM variable_name
;
It does not permit dynamic references such as ''&$classname::foo'', due to conflicts with existing syntax and for symmetry (while ''&$classname::foo'' would be doable, ''&FooBar::$foo'' is not, so we do neither).
When a function is referenced in this manner, an unbound, unscoped ''Closure'' of that function is returned. For a static method, a static, scoped ''Closure'' is given. When a normal method is referenced, an unbound, scoped ''Closure'' is given.
We relax the restriction on unbound scoped closures. This is because if we were to give a static method, it could not be bound (useless as it is an instance method), and we don't know what to bind to ahead-of-time. Thus we create an incomplete closure of sorts, which can be called and probably won't work (much like you can statically call an instance method), or can be bound with ''->bindTo'' or ''->call''. We don't provide any way to produce an unbound scoped closure in userland as this is, aside from function referencing, an obscure use case.
Because the ''->call'' method would be useful here, this RFC depends on [[rfc:closure_apply|that RFC]] passing first, and the patch incorporates the ''->call'' patch.
===== Backward Incompatible Changes =====
None.
===== Proposed PHP Version(s) =====
Next PHP 5.x, or next PHP X (PHP 7), whichever comes first.
===== Future Scope =====
There is no future scope to this RFC.
===== Proposed Voting Choices =====
As this adds to the language itself, a 2/3 majority is required. A straight Yes/No vote is to be held.
===== Patches and Tests =====
A branch which implements this with a test which incorporates the Closure::call patch and is based on master can be found here: https://github.com/TazeTSchnitzel/php-src/compare/function_reference_with_apply
===== References =====
* [[https://en.wikipedia.org/wiki/First-class_function|First-class function - Wikipedia]]
* [[rfc:closure_apply|Closure::call RFC]]