- positional argument:
f(expr)
- named argument:
f(x: expr)
- variadic unpacking:
f(...expr)
- positional placeholder:
f(?)
- named placeholder:
f(x: ?)
- variadic placeholder:
f(...)
When any argument of an argument list is a placeholder, then instead of doing a function call it will become a partial function application. A partial function application results in a Closure. The "applied function" is the callable which the resulting Closure will call when it is invoked, passing along the arguments it bound at creation time along with the arguments it receives at invocation time. The created Closure will return the result of the calling the applied function (TODO: new operator)
"To bind" an argument means that the the parameter at that position or name in the applied function is now bound and cannot be used by another binding nor placeholders. Consider this snippet:
function f($x = 0, $y = 0) {}
f(?, $value);
The argument $value
is bound to the position of $y
in function f
, and $y
cannot participate in placeholders nor other bindings. For example, this is illegal because $y
is bound by both position and by name: f(?, $value, y: $value2)
).
- From left to right, examine each argument.
- If it's a positional argument, bind it to the same position in the delegating invocation.
- If it's a named argument, bind it to the position in the delegating invocation which corresponds to the parameter of the same name in the applied function if it exists, otherwise bind the named argument to the delegating invocation as a trailing named argument.
- If it's a positional placeholder, create a parameter in the resulting closure and bind the created parameter to the position in the delegating call that corresponds to the placeholder's position (not the created parameter's position). The created parameter has the same name, type, and refness as the corresponding position in the applied function.
- If it's a named placeholder, create a parameter in the resulting closure and bind it to the position of the parameter in the applied function which has the same name. If the applied function does not have a parameter of that name, bind it as a trailing named argument. The created parameter has the same type and refness as the parameter with the same name in the applied function.
- If it's the variadic placeholder, create parameters in the resulting closure which correspond to the remaining parameters in the applied function. If the applied function is variadic, also create a varaidic parameter of the same name, type, and refness as the variadic parameter in the applied function.
- Error if:
- The same parameter is ever used twice, such as by name and position or used by name twice.
- The name of a named placeholder does not exist in the applied function, unless the applied function is variadic.
- A required parameter of the applied function lacks a placeholder or binding.
- The variadic placeholder is placed anywhere except in the last argument.
- Positional placeholders or positional arguments are used after named placeholders or named arguments.
function rectangle_volume(float $length, float $width, float $height): float {
return $length * $width * $height;
}
// variables of the same name are equivalent
$A = rectangle_volume(?, ?, ?);
$A = rectangle_volume(...);
$A = fn(float $length, float $width, float $height): float
=> rectangle_volume($length, $width, $height);
$B = rectangle_volume(height: ?, length: ?, width: ?);
$B = fn(float $height, float $length, float $width): float
=> rectangle_volume($length, $width, $height);
// Error because $length is used by name and position
// $C = rectangle_volume(?, length: ?, width: 1, height: 1);
// Error because $width and $height are required parameters and have neither
// placeholders nor bindings.
// $D = rectangle_volume(?); // use rectangle_volume(...) instead
Partial application creates a Closure that calls the applied function and returns the result of calling the applied function. This works for many kinds of "functions":
If you partially apply a function, you get a Closure which will call that function.
If you partially apply an instance/static method, you will get a Closure that calls the instance/static method. TODO: flesh out scope.
If you partially apply an new operator, you will get a Closure that calls the new operator. TODO: anonymous classes.
If you partially apply Closure A, then you get another Closure A-prime that calls the Closure A.
If you partially apply a callable from a string or special array form, then you get a Closure which calls the callable of the string or special array form. TODO: flesh out edges here.