This is an old revision of the document!
PHP RFC: Partial Function Application
- Version: 1.0
- Date: 2019-02-05
- Author: Paul Crovella, Levi Morrison
- Status: Draft
- First Published at: https://wiki.php.net/rfc/partial_function_application
Introduction
Partial function application is the process of binding only some of the arguments to a function call and leaving the remainder to be bound a later point. In PHP this would be done by a closure.
Proposal
Support partial function application via the argument placeholders ?
and ...
. Any function or method call in which one or more of the arguments is an argument placeholder instead results in a closure being returned which wraps the called function, fixing the given (non-placeholder) arguments.
Type declarations, parameter names, reference parameters, optional defaults, variadics are all taken on by the closure from the function definition. All arguments given to a closure, including those beyond what are specified in the signature, get passed along to the underlying function.
The basic idea is to use a partial function call to stamp out a copy of the function, sans parameters where fixed arguments have already been given.
Types
Type declarations are retained, as are parameter names (e.g. for reflection.)
function f(int $x, int $y): int {} $partialFoo = f(?, 42);
is equivalent to
$partial = function(int $x): int { return f($x, 42); };
Variables/References
Variable arguments are use
d, and done so by reference if specified in the called function definition.
function f($value, &$ref) {} $array = ['arg' => 0]; $f = f(?, $array['arg']);
is equivalent to
$ref = &$array['arg']; $f = function($value) use (&$ref) { return f($value, $ref); };
Optional Parameters
Optional parameters remain optional, inheriting defaults.
function f($a = 0, $b = 1) {} $f = f(?, 2);
is equivalent to
$partial = function($a = 0) { return f($a, 2); };
func_num_args et al.
In order to keep func_num_args
and friends happy, the arguments passed through will only be those up to and including the right-most actually given - whether at call time or during partial application. In other words, trailing optional parameters in the closure will not have their resolved defaults sent to the underlying function.
function f($a = 0, $b = 0, $c = 0, $d = 0) { echo func_num_args(); } $f = f(?, 1, ?, ?); $f(0, 2); // prints 3 $f(); // prints 2
Extra Arguments
You can pass as many additional arguments as you like when calling a function in PHP, beyond what's specified in the signature. When you pass extra arguments to the closure, or during partial application, they are passed along to the wrapped function.
function f($a, $b) { print_r(func_get_args()); } $f = f(?, 2);
is actually akin to:
$f = function($a, ...$extraArgs) { return f($a, 2, ...$extraArgs); };
(Though without unnecessary packing/unpacking, or $extraArgs
appearing in reflection.)
So calling: $f(1, 3, 4)
results in:
Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 )
Trailing Placeholders
Continuing on that, it is not required to include placeholders for trailing arguments, though at least one placeholder somewhere is necessary to indicate partial application. This is primarily to ease partial application of functions with long lists of optional parameters.
function f($m, $n, $o, $x = 0, $y = 0, $z = 0) {} $f = f(1, ?);
is roughly (minding the func_num_args
section above) equivalent to
$f = function($n, $o, $x = 0, $y = 0, $z = 0) { return f(1, $n, $o, $x, $y, $z); };
In order to better convey to a reader that more arguments are expected, a trailing ...
may be used in addition to or in lieu of ?
placeholders.
So these are equivalent:
$f = f(?, 1); $f = f(?, 1, ...);
As are these:
$f = f(1, ?); $f = f(1, ?, ...); $f = f(1, ...);
Extra Trailing Placeholders
Additional placeholders beyond a function's signature do not cause an error and have no additional impact, though effectively allow you to apply all arguments to a function to be called later.
$n = 1000000; $log10ofn = log10($n, ?);
is equivalent to
$log10ofn = function() use ($n) { return log10($n); }
Variadic Functions
Variadic functions retain their signatures. Placeholders in a variadic position remain optional in the closure (there are no defaults, places not filled are simply not included in what's sent to the underlying function.) Fixed and placeholder arguments can be interleaved together.
function f(...$args) { print_r($args); } $f = f(?, 2, ?, 4, ?, 6); $f(1, 3);
Would output:
Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 6 )
Backward Incompatible Changes
None.
Proposed PHP Version(s)
7.4 or 8
Vote
Yes or no, requiring a 2/3 majority to pass.