[RFC] Partial function application

php.internals

Larry Garfield

5 years ago
Greetings, Internalians! I would like to offer for your consideration another RFC, specifically syntax for partial function application. https://wiki.php.net/rfc/partial_function_application It includes an implementation by Joe Watkins that is already about 95% complete. (There's some edge cases he's still sorting out, but all of the typical cases should work already.) Most of the design work comes from Levi Morrison and Paul Crovella. I helped out with the tests, a few edge bits, and general instigator/nudge. :-) Discuss.
-- Larry Garfield larry@garfieldtech.com

Olle Härstedt

5 years ago
2021-04-25 21:25 GMT+02:00, Larry Garfield <larry@garfieldtech.com>:
> Greetings, Internalians! > > I would like to offer for your consideration another RFC, specifically > syntax for partial function application. > > https://wiki.php.net/rfc/partial_function_application > > It includes an implementation by Joe Watkins that is already about 95% > complete. (There's some edge cases he's still sorting out, but all of the > typical cases should work already.) Most of the design work comes from Levi > Morrison and Paul Crovella. I helped out with the tests, a few edge bits, > and general instigator/nudge. :-) > > Discuss. > > -- > Larry Garfield > larry@garfieldtech.com > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > >
Nice. :) Are there any other motivating use-cases besides pipe operator (that are relevant for web dev)? Olle

Max Semenik

5 years ago
On Sun, Apr 25, 2021 at 10:26 PM Larry Garfield <larry@garfieldtech.com> wrote:
> https://wiki.php.net/rfc/partial_function_application
Amazing, I've wanted this for so long! Have you considered extending this syntax to OOP, e.g. $obj->method(?) or SomeClass::staticMethod(?)? On Sun, Apr 25, 2021 at 10:51 PM Olle Härstedt <olleharstedt@gmail.com> wrote:
> Nice. :) Are there any other motivating use-cases besides pipe > operator (that are relevant for web dev)?
One reason I'm personally excited about this proposal is that it would allow static analysis in more cases, e.g. $param = 'fooBar'; // What's this string? A user name? Elon Musk's favorite color? Text of US Constitution? somefunc($param); function somefunc($cb) { $cb(123, 'meh'); // What parameter types does this accept? } Allowing syntax like $param = fooBar(?, ?); would indicate clearly what this variable receives. Analysis tools would then be able to check if such a function exists and whether it would receive parameters of expected types.
-- Best regards, Max Semenik

Olle Härstedt

5 years ago
2021-04-25 21:07 GMT, Max Semenik <maxsem.wiki@gmail.com>:
> On Sun, Apr 25, 2021 at 10:26 PM Larry Garfield <larry@garfieldtech.com> > wrote: > >> https://wiki.php.net/rfc/partial_function_application > > > Amazing, I've wanted this for so long! Have you considered extending this > syntax to OOP, e.g. $obj->method(?) or SomeClass::staticMethod(?)? > > On Sun, Apr 25, 2021 at 10:51 PM Olle Härstedt <olleharstedt@gmail.com> > wrote: > >> Nice. :) Are there any other motivating use-cases besides pipe >> operator (that are relevant for web dev)? > > > One reason I'm personally excited about this proposal is that it would > allow static analysis in more cases, e.g. > > $param = 'fooBar'; // What's this string? A user name? Elon Musk's favorite > color? Text of US Constitution? > > somefunc($param); > > function somefunc($cb) { > $cb(123, 'meh'); // What parameter types does this accept? > } > > Allowing syntax like $param = fooBar(?, ?); would indicate clearly what > this variable receives. Analysis tools would then be able to check if such > a function exists and whether it would receive parameters of expected > types.
Like an alternative to value objects (`class Email ... `)? Not sure I understand how, sorry. Olle

Olle Härstedt

5 years ago
2021-04-25 21:07 GMT, Max Semenik <maxsem.wiki@gmail.com>:
> On Sun, Apr 25, 2021 at 10:26 PM Larry Garfield <larry@garfieldtech.com> > wrote: > >> https://wiki.php.net/rfc/partial_function_application > > > Amazing, I've wanted this for so long! Have you considered extending this > syntax to OOP, e.g. $obj->method(?) or SomeClass::staticMethod(?)? > > On Sun, Apr 25, 2021 at 10:51 PM Olle Härstedt <olleharstedt@gmail.com> > wrote: > >> Nice. :) Are there any other motivating use-cases besides pipe >> operator (that are relevant for web dev)? > > > One reason I'm personally excited about this proposal is that it would > allow static analysis in more cases, e.g. > > $param = 'fooBar'; // What's this string? A user name? Elon Musk's favorite > color? Text of US Constitution? > > somefunc($param); > > function somefunc($cb) { > $cb(123, 'meh'); // What parameter types does this accept? > } > > Allowing syntax like $param = fooBar(?, ?); would indicate clearly what > this variable receives. Analysis tools would then be able to check if such > a function exists and whether it would receive parameters of expected > types. > > -- > Best regards, > Max Semenik >
By the way, for pipe operators to be really useful, it should probably be possible to combine with middleware. Olle

Larry Garfield

5 years ago
On Sun, Apr 25, 2021, at 4:34 PM, Olle Härstedt wrote:
> By the way, for pipe operators to be really useful, it should probably > be possible to combine with middleware. > > Olle
Pipes are their own RFC I plan to bring back up after this one gets closer to a vote or passes, as they complement each other nicely. I'm not sure what you mean here, though. Pipes *are* a middleware style, just a different approach than the deeply nested call stack that is typical today. --Larry Garfield

Olle Härstedt

5 years ago
2021-04-26 0:19 GMT+02:00, Larry Garfield <larry@garfieldtech.com>:
> On Sun, Apr 25, 2021, at 4:34 PM, Olle Härstedt wrote: > > >> By the way, for pipe operators to be really useful, it should probably >> be possible to combine with middleware. >> >> Olle > > Pipes are their own RFC I plan to bring back up after this one gets closer > to a vote or passes, as they complement each other nicely. I'm not sure > what you mean here, though. Pipes *are* a middleware style, just a > different approach than the deeply nested call stack that is typical today. > > --Larry Garfield
I was thinking of middleware as defined in PSR (since that's what middleware frameworks are built on). Would be nice with a connection there. Olle

Unnamed Person

5 years ago
On Sun, Apr 25, 2021 at 3:08 PM Max Semenik <maxsem.wiki@gmail.com> wrote:
> > On Sun, Apr 25, 2021 at 10:26 PM Larry Garfield <larry@garfieldtech.com> > wrote: > > > https://wiki.php.net/rfc/partial_function_application > > > Amazing, I've wanted this for so long! Have you considered extending this > syntax to OOP, e.g. $obj->method(?) or SomeClass::staticMethod(?)?
It works for methods as well, as stated in the proposal section. I think there aren't any examples in the RFC just because functions make for shorter examples ^_^ but we should add some to help make that clear.

Larry Garfield

5 years ago
On Sun, Apr 25, 2021, at 4:34 PM, Levi Morrison via internals wrote:
> On Sun, Apr 25, 2021 at 3:08 PM Max Semenik <maxsem.wiki@gmail.com> wrote: > > > > On Sun, Apr 25, 2021 at 10:26 PM Larry Garfield <larry@garfieldtech.com> > > wrote: > > > > > https://wiki.php.net/rfc/partial_function_application > > > > > > Amazing, I've wanted this for so long! Have you considered extending this > > syntax to OOP, e.g. $obj->method(?) or SomeClass::staticMethod(?)? > > It works for methods as well, as stated in the proposal section. I > think there aren't any examples in the RFC just because functions make > for shorter examples ^_^ but we should add some to help make that > clear.
Ha, yes, I didn't realize we were missing those. :-) I just added a Methods section. In short, yes, it works for methods, functions, closures, basically anything you can otherwise call. --Larry Garfield

Unnamed Person

5 years ago
On Sun, Apr 25, 2021 at 1:51 PM Olle Härstedt <olleharstedt@gmail.com> wrote:
> > 2021-04-25 21:25 GMT+02:00, Larry Garfield <larry@garfieldtech.com>: > > Greetings, Internalians! > > > > I would like to offer for your consideration another RFC, specifically > > syntax for partial function application. > > > > https://wiki.php.net/rfc/partial_function_application > > > > It includes an implementation by Joe Watkins that is already about 95% > > complete. (There's some edge cases he's still sorting out, but all of the > > typical cases should work already.) Most of the design work comes from Levi > > Morrison and Paul Crovella. I helped out with the tests, a few edge bits, > > and general instigator/nudge. :-) > > > > Discuss. > > > > -- > > Larry Garfield > > larry@garfieldtech.com > > > > -- > > PHP Internals - PHP Runtime Development Mailing List > > To unsubscribe, visit: https://www.php.net/unsub.php > > > > > > Nice. :) Are there any other motivating use-cases besides pipe > operator (that are relevant for web dev)? > > Olle > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php >
Yes, of course! PHP has many uses of `array_map`, `array_filter`, etc, that take a callback as a parameter. It's hard to create good examples on the spot, but here's an example, at least: array_filter($array, in_array(?, $haystack, strict: true))

Lynn

5 years ago
On Sun, Apr 25, 2021 at 9:26 PM Larry Garfield <larry@garfieldtech.com> wrote:
> Greetings, Internalians! > > I would like to offer for your consideration another RFC, specifically > syntax for partial function application. > > https://wiki.php.net/rfc/partial_function_application > > It includes an implementation by Joe Watkins that is already about 95% > complete. (There's some edge cases he's still sorting out, but all of the > typical cases should work already.) Most of the design work comes from > Levi Morrison and Paul Crovella. I helped out with the tests, a few edge > bits, and general instigator/nudge. :-) > > Discuss. > > -- > Larry Garfield > larry@garfieldtech.com > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > >
Heya, this is an interesting feature! The way I understand it, it adds the ability to turn a call to an object method into a callable? ```php class CategorizedThings { private array $theThings = []; public function addOneThing(mixed $thing, string $category): void { $this->theThings[$category] = $thing; } } $things = new CategorizedThings(); $adder = $things->addOneThing(?, 'a category'); foreach ($repository->findAll() as $aThing) { $adder($aThing); } // which would be the same as: $things = new CategorizedThings(); $adder = function (mixed $aThing) use ($things) { $things->addOneThing($aThing, 'a category'); } foreach ($repository->findAll() as $aThing) { $adder($aThing); } ``` I assume the following would also be possible? ```php class Something { public static function toString(mixed $v): string { return (string) $v; } } function toString(mixed $v) : string { return (string) $v; } array_map(Something::toString(?), [1, 2, 3]); array_map(toString(?), [1, 2, 3]); // instead of array_map([Something::class, 'toString'], [1, 2, 3]) array_map('toString', [1, 2, 3]); ``` If this is indeed the scenario, would it be worth adding a syntax to indicate _all_ parameters can be placeholders? When a signature has multiple arguments, `toString(?, ?, ?, ?)` could become `toString(...?)` or maybe something like `toString(*)`?

Unnamed Person

5 years ago
> If this is indeed the scenario, would it be worth adding a syntax to > indicate _all_ parameters can be placeholders? When a signature has > multiple arguments, `toString(?, ?, ?, ?)` could become `toString(...?)` or > maybe something like `toString(*)`?
At the moment, there isn't any difference in `foo(?)` and `foo(?, ?, ?, ?, ?, ?, ?, ?)`. If there is one placeholder, then it will bind (or fix) the non-placeholder arguments and then create a closure-like with a signature of the remaining parameters. Adding more placeholders does not change anything, except to adjust positions: foo(?, $bar, ?, $baz) In this case the two placeholders are meaningful because they adjust which positional arguments are bound. This is the only difference in behavior in one placeholder or more. In an earlier version of the RFC I had `...` so you could write `foo(...)`. Some of the RFC authors didn't like this, because it was functionally no different and brought up "when to use ? and when to use ..." as a question.

Larry Garfield

5 years ago
On Sun, Apr 25, 2021, at 2:25 PM, Larry Garfield wrote:
> Greetings, Internalians! > > I would like to offer for your consideration another RFC, specifically > syntax for partial function application. > > https://wiki.php.net/rfc/partial_function_application > > It includes an implementation by Joe Watkins that is already about 95% > complete. (There's some edge cases he's still sorting out, but all of > the typical cases should work already.) Most of the design work comes > from Levi Morrison and Paul Crovella. I helped out with the tests, a > few edge bits, and general instigator/nudge. :-) > > Discuss.
It looks like the conversation has died down, and it's been two weeks, so pending any other notable feedback I'll open a vote on this RFC on Thursday or Friday. --Larry Garfield

Nicolas Grekas

5 years ago
> On Sun, Apr 25, 2021, at 2:25 PM, Larry Garfield wrote: > > Greetings, Internalians! > > > > I would like to offer for your consideration another RFC, specifically > > syntax for partial function application. > > > > https://wiki.php.net/rfc/partial_function_application > > > > It includes an implementation by Joe Watkins that is already about 95% > > complete. (There's some edge cases he's still sorting out, but all of > > the typical cases should work already.) Most of the design work comes > > from Levi Morrison and Paul Crovella. I helped out with the tests, a > > few edge bits, and general instigator/nudge. :-) > > > > Discuss. > > It looks like the conversation has died down, and it's been two weeks, so > pending any other notable feedback I'll open a vote on this RFC on Thursday > or Friday. > > --Larry Garfield >
LGTM, thanks for the RFC! What about visibility? I suppose this works even outside the object scope? should this be mentionned? $foo = $this->somePrivateMethod(1, ?) Would it make sense to support a way to use a placeholder for "all remaining args"? Eg: $foo = some_func(1, ...?) Combined with my previous comment, this could replace Closure::fromCallable() by a language construct, which is something that we already discussed in another thread (we talked about some ::function special suffix instead): $foo = $this->somePrivateMethod(...?) In this last form, we might make this result in Closure::fromCallable() exactly. Aka no increase of the depth of the stack trace. BTW, ideally, partial functions should not increase the depth of the stacktrace at all. Do they? Nicolas

Larry Garfield

5 years ago
On Tue, May 11, 2021, at 11:57 AM, Nicolas Grekas wrote:
> > On Sun, Apr 25, 2021, at 2:25 PM, Larry Garfield wrote: > > > Greetings, Internalians! > > > > > > I would like to offer for your consideration another RFC, specifically > > > syntax for partial function application. > > > > > > https://wiki.php.net/rfc/partial_function_application > > > > > > It includes an implementation by Joe Watkins that is already about 95% > > > complete. (There's some edge cases he's still sorting out, but all of > > > the typical cases should work already.) Most of the design work comes > > > from Levi Morrison and Paul Crovella. I helped out with the tests, a > > > few edge bits, and general instigator/nudge. :-) > > > > > > Discuss. > > > > It looks like the conversation has died down, and it's been two weeks, so > > pending any other notable feedback I'll open a vote on this RFC on Thursday > > or Friday. > > > > --Larry Garfield > > > > LGTM, thanks for the RFC! > > What about visibility? I suppose this works even outside the object scope? > should this be mentionned? > $foo = $this->somePrivateMethod(1, ?)
We're pretty sure it will work, and if you return $foo to another caller and then call it, it will work fine. We'll add a test to confirm that.
> Would it make sense to support a way to use a placeholder for "all > remaining args"? > Eg: > $foo = some_func(1, ...?)
That's already implicit in the way it works now. If you placeholder at least one parameter, and then don't fill in all of the remaining parameters, any additional trailing parameters are always placeholdered. So $obj->foo(?) will always return a partial that has the same arity as foo() does, regardless of how many parameters it has.
> Combined with my previous comment, this could replace > Closure::fromCallable() by a language construct, which is something that we > already discussed in another thread (we talked about some ::function > special suffix instead): > $foo = $this->somePrivateMethod(...?)
Yep. That's noted in the RFC. Although not the primary intent, a nice side effect is that any_func(?) is now a safe way to turn any function/method into a callable to reference it. That renders ::function or similar 98% unnecessary. (The remaining cases are mostly where you need to collect data statically for compilation or similar.)
> In this last form, we might make this result in Closure::fromCallable() > exactly. Aka no increase of the depth of the stack trace. > > BTW, ideally, partial functions should not increase the depth of the > stacktrace at all. Do they? > > Nicolas
They currently do, since they work by creating a Closure-esque object called Partial with an __invoke() method. However, if you partial the same thing multiple times then only one stack level gets added. That is: function test($a, $b, $c, $d) { throw new Exception(); } $first = test(?, ?, ?, ?); $second = $first(1, ?); $third = $second(2, ?); $fourth = $third(3, ?); $result = $fourth(4); The stack at the end will contain only one partial "level", not 4. --Larry Garfield

Nicolas Grekas

5 years ago
> > > On Sun, Apr 25, 2021, at 2:25 PM, Larry Garfield wrote: > > > > Greetings, Internalians! > > > > > > > > I would like to offer for your consideration another RFC, > specifically > > > > syntax for partial function application. > > > > > > > > https://wiki.php.net/rfc/partial_function_application > > > > > > > > It includes an implementation by Joe Watkins that is already about > 95% > > > > complete. (There's some edge cases he's still sorting out, but all > of > > > > the typical cases should work already.) Most of the design work > comes > > > > from Levi Morrison and Paul Crovella. I helped out with the tests, a > > > > few edge bits, and general instigator/nudge. :-) > > > > > > > > Discuss. > > > > > > It looks like the conversation has died down, and it's been two weeks, > so > > > pending any other notable feedback I'll open a vote on this RFC on > Thursday > > > or Friday. > > > > > > --Larry Garfield > > > > > > > LGTM, thanks for the RFC! > > > > What about visibility? I suppose this works even outside the object > scope? > > should this be mentionned? > > $foo = $this->somePrivateMethod(1, ?) > > We're pretty sure it will work, and if you return $foo to another caller > and then call it, it will work fine. We'll add a test to confirm that. > > > Would it make sense to support a way to use a placeholder for "all > > remaining args"? > > Eg: > > $foo = some_func(1, ...?) > > That's already implicit in the way it works now. If you placeholder at > least one parameter, and then don't fill in all of the remaining > parameters, any additional trailing parameters are always placeholdered. > So $obj->foo(?) will always return a partial that has the same arity as > foo() does, regardless of how many parameters it has. > > > Combined with my previous comment, this could replace > > Closure::fromCallable() by a language construct, which is something that > we > > already discussed in another thread (we talked about some ::function > > special suffix instead): > > $foo = $this->somePrivateMethod(...?) > > Yep. That's noted in the RFC. Although not the primary intent, a nice > side effect is that any_func(?) is now a safe way to turn any > function/method into a callable to reference it. That renders ::function > or similar 98% unnecessary. (The remaining cases are mostly where you need > to collect data statically for compilation or similar.) >
Great news!
> In this last form, we might make this result in Closure::fromCallable() > > exactly. Aka no increase of the depth of the stack trace. > > > > BTW, ideally, partial functions should not increase the depth of the > > stacktrace at all. Do they? > > > > Nicolas > > They currently do, since they work by creating a Closure-esque object > called Partial with an __invoke() method. However, if you partial the same > thing multiple times then only one stack level gets added. >
Nice. Would it be possible to optimize this and remove the extra frame? At least maybe for the (?) case? This makes me wonder: can we create a partial programmatically? Wouldn't that be needed for some use cases? Partial::createFromCallable($callable, the-args)? Nicolas

Larry Garfield

5 years ago
On Tue, May 11, 2021, at 1:32 PM, Nicolas Grekas wrote:
> > > BTW, ideally, partial functions should not increase the depth of the > > > stacktrace at all. Do they? > > > > > > Nicolas > > > > They currently do, since they work by creating a Closure-esque object > > called Partial with an __invoke() method. However, if you partial the same > > thing multiple times then only one stack level gets added. > > > > Nice. Would it be possible to optimize this and remove the extra frame? At > least maybe for the (?) case?
I'd have to defer to Joe (he wrote the implementation), but I suspect not. The partial has to be there to carry around the information of what callable to actually call and what the bound parameters are. At that point, it's likely more work to decompose the Partial internally before calling it than to just call the Partial itself.
> This makes me wonder: can we create a partial programmatically? Wouldn't > that be needed for some use cases? > Partial::createFromCallable($callable, the-args)? > > Nicolas
I cannot think of a use case where that would be needed. Since you can partial-ize any callable, including a dynamic one, if you needed to do something like partial-ize one of a series of function calls you can do that already: $c = match($some_input) { 'A' => 'func_a', 'B' => 'func_b', 'C' => 'func_c', }; $p = $c(1, 2 ?, 4); Though at that point, just partialing them in the first place inside the match would be better as then you never have a function name in a string to begin with. --Larry Garfield

Nicolas Grekas

5 years ago
> > > > BTW, ideally, partial functions should not increase the depth of the > > > > stacktrace at all. Do they? > > > > > > > > Nicolas > > > > > > They currently do, since they work by creating a Closure-esque object > > > called Partial with an __invoke() method. However, if you partial the > same > > > thing multiple times then only one stack level gets added. > > > > > > > Nice. Would it be possible to optimize this and remove the extra frame? > At > > least maybe for the (?) case? > > I'd have to defer to Joe (he wrote the implementation), but I suspect > not. The partial has to be there to carry around the information of what > callable to actually call and what the bound parameters are. At that > point, it's likely more work to decompose the Partial internally before > calling it than to just call the Partial itself. >
Then I suppose that removing the extra frame would mean embedding that state into Closure objects. Would it make sense to extend Closure this way? Isn't the new Partial class an implementation artefact that should be removed? If not, what are the userland-side reasons to keep it? If the Partial class has to be kept, can it be optimized out back to a closure when no arguments are bound? (aka the "$closure = $this->method(?)" case)
> > This makes me wonder: can we create a partial programmatically? Wouldn't > > that be needed for some use cases? > > Partial::createFromCallable($callable, the-args)? > > > > Nicolas > > I cannot think of a use case where that would be needed. Since you can > partial-ize any callable, including a dynamic one, if you needed to do > something like partial-ize one of a series of function calls you can do > that already: > > $c = match($some_input) { > 'A' => 'func_a', > 'B' => 'func_b', > 'C' => 'func_c', > }; > > $p = $c(1, 2 ?, 4); > > Though at that point, just partialing them in the first place inside the > match would be better as then you never have a function name in a string to > begin with. >
Here is a use case: high-order argument resolvers / function reducers. What I mean is a function that takes a callable as arguments, resolves as many args of the callable as it can using whatever logic fits, and returns a callable with fewer arguments (only the non-resolved ones - aka a Partial). Nicolas

Larry Garfield

5 years ago
On Wed, May 12, 2021, at 4:51 AM, Nicolas Grekas wrote:
> > > This makes me wonder: can we create a partial programmatically? Wouldn't > > > that be needed for some use cases? > > > Partial::createFromCallable($callable, the-args)? > > > > > > Nicolas > > > > I cannot think of a use case where that would be needed. Since you can > > partial-ize any callable, including a dynamic one, if you needed to do > > something like partial-ize one of a series of function calls you can do > > that already: > > > > $c = match($some_input) { > > 'A' => 'func_a', > > 'B' => 'func_b', > > 'C' => 'func_c', > > }; > > > > $p = $c(1, 2 ?, 4); > > > > Though at that point, just partialing them in the first place inside the > > match would be better as then you never have a function name in a string to > > begin with. > > > > Here is a use case: high-order argument resolvers / function reducers. > > What I mean is a function that takes a callable as arguments, resolves as > many args of the callable as it can using whatever logic fits, and returns > a callable with fewer arguments (only the non-resolved ones - aka a > Partial).
You're thinking something like an auto-wiring routine for callables? function volume(int $x, int $y, int $z) { ... } class Resolver { private array $context = ['x' => 5]; public resolve(callable $c) { foreach (get_argument_names_from_param($c) as $k) { if (isset($this->context[$k]) { $args[$k] = $this->context[$k]; } } return $c(...$args, ?); } } $r = new Resolver(); $c2 = $r->resolve(volume(?)); print $c2(y: 3, z: 9); I haven't tried running that (I don't feel like messing with the necessary reflection), but... I think it would work already? Whether that's useful in practice or not I don't know. :-) --Larry Garfield

Nicolas Grekas

5 years ago
> > > > > This makes me wonder: can we create a partial programmatically? > Wouldn't > > > > that be needed for some use cases? > > > > Partial::createFromCallable($callable, the-args)? > > > > > > > > Nicolas > > > > > > I cannot think of a use case where that would be needed. Since you can > > > partial-ize any callable, including a dynamic one, if you needed to do > > > something like partial-ize one of a series of function calls you can do > > > that already: > > > > > > $c = match($some_input) { > > > 'A' => 'func_a', > > > 'B' => 'func_b', > > > 'C' => 'func_c', > > > }; > > > > > > $p = $c(1, 2 ?, 4); > > > > > > Though at that point, just partialing them in the first place inside > the > > > match would be better as then you never have a function name in a > string to > > > begin with. > > > > > > > Here is a use case: high-order argument resolvers / function reducers. > > > > What I mean is a function that takes a callable as arguments, resolves as > > many args of the callable as it can using whatever logic fits, and > returns > > a callable with fewer arguments (only the non-resolved ones - aka a > > Partial). > > You're thinking something like an auto-wiring routine for callables? > > function volume(int $x, int $y, int $z) { ... } > > class Resolver { > private array $context = ['x' => 5]; > > public resolve(callable $c) { > foreach (get_argument_names_from_param($c) as $k) { > if (isset($this->context[$k]) { > $args[$k] = $this->context[$k]; > } > } > return $c(...$args, ?); > } > } > > $r = new Resolver(); > $c2 = $r->resolve(volume(?)); > > print $c2(y: 3, z: 9); > > I haven't tried running that (I don't feel like messing with the necessary > reflection), but... I think it would work already? Whether that's useful > in practice or not I don't know. :-) >
Oh, indeed, that's super great! Then remains my question about replacing Partial by Closure. I think this would be an important feature and that would remove the need for ReflectionPartial. About reflection, the following behaviors are quite problematic to me:
> ReflectionFunction(Closure::fromCallable($arbitrary_callable)) to
normalize all callables to a single reflection interface will no longer work I don't understand why. Unless there is a rationale to that (which one?) this looks arbitrary. I very often see code like: if (!$callable instanceof Closure) $callable = Closure::fromCallable($callable); $r = ReflectionFunction($callable) The limitation of described in the RFC will make the equivalent boilerplate much more complex in order to support 8.1 I would like this to be improved or at least better explained. Cheers, Nicolas

Alexandru Pătrănescu

5 years ago
Hi Larry, On Tue, May 11, 2021 at 8:55 PM Larry Garfield <larry@garfieldtech.com> wrote:
> > They currently do, since they work by creating a Closure-esque object > called Partial with an __invoke() method. > > --Larry Garfield > >
1. Would it be possible to mention the `Partial` class in the RFC? From what I understand this is what it will be returned by the `get_class($partial)`. I guess the class will be final (similar with Closure) and will only have a private constructor and a public __invoke method. 2. As now ReflectionFunction accepts Closure|string, wouldn't it be simpler for the users to just have it accept Closure|Partial|string to avoid introducing another reflection class and also avoid the userland issue you mentioned? Alex

David Gebler

5 years ago
On Tue, May 11, 2021 at 4:39 PM Larry Garfield <larry@garfieldtech.com> wrote:
> It looks like the conversation has died down, and it's been two weeks, so > pending any other notable feedback I'll open a vote on this RFC on Thursday > or Friday. > > --Larry Garfield > > >
My only query / point of consideration is a very minor one for what is otherwise an enthusiastic (non-voting) +1. In relation to one of the examples: function whole($one, $two) { /* ... */ } // equivalent to calling whole(1, 2, 3) $result = whole(?, 2)(1, 3); Is there a risk this has the potential to be confusing? We've always had this quirk in PHP of being able to pass extra unspecified parameters in function calls, but with the exception of variadic functions the expectation is that they are ignored. Arguably if I saw this: function whole($one, $two) { /* ... */ } $partial = whole(?, 2); $result = partial(1, 3); I might expect it to semantically translate to: function partial($one) { return whole($one, 2); } with the extra parameter, 3, ignored as per convention for any other function receiving undefined extra params. But the RFC says this parameter would be passed to whole(). Which is not unreasonable, but yeah I guess it strikes me it's kind of a quirk in itself to do it that way. -Dave

Björn Larsson

5 years ago
Den 2021-05-11 kl. 17:38, skrev Larry Garfield:
> On Sun, Apr 25, 2021, at 2:25 PM, Larry Garfield wrote: >> Greetings, Internalians! >> >> I would like to offer for your consideration another RFC, specifically >> syntax for partial function application. >> >> https://wiki.php.net/rfc/partial_function_application >> >> It includes an implementation by Joe Watkins that is already about 95% >> complete. (There's some edge cases he's still sorting out, but all of >> the typical cases should work already.) Most of the design work comes >> from Levi Morrison and Paul Crovella. I helped out with the tests, a >> few edge bits, and general instigator/nudge. :-) >> >> Discuss. > > It looks like the conversation has died down, and it's been two weeks, so pending any other notable feedback I'll open a vote on this RFC on Thursday or Friday. > > --Larry Garfield >
Sometimes the RFC's mentions how / if a specific feature is used in other languages. Would it be beneficial also to this RFC? I also think that the Introduction / Proposal paragraph could benefit from a few lines on why this feature is needed. The "pipe" use case further down is a good motiovation, just wondering about others. r//Björn L

Larry Garfield

5 years ago
On Tue, May 11, 2021, at 10:38 AM, Larry Garfield wrote:
> On Sun, Apr 25, 2021, at 2:25 PM, Larry Garfield wrote: > > Greetings, Internalians! > > > > I would like to offer for your consideration another RFC, specifically > > syntax for partial function application. > > > > https://wiki.php.net/rfc/partial_function_application > > > > It includes an implementation by Joe Watkins that is already about 95% > > complete. (There's some edge cases he's still sorting out, but all of > > the typical cases should work already.) Most of the design work comes > > from Levi Morrison and Paul Crovella. I helped out with the tests, a > > few edge bits, and general instigator/nudge. :-) > > > > Discuss. > > It looks like the conversation has died down, and it's been two weeks, > so pending any other notable feedback I'll open a vote on this RFC on > Thursday or Friday.
Naturally there was more conversation. :-) But good conversation, so the RFC has been updated again. I recommend everyone look it over another time. Highlights: * Joe was able to figure out how to fold the extra partial logic into the Closure object. So the Partial and ReflectionPartial classes are gone; partial application gives you a closure object that looks like any other, but still manages to optimize away repeated partial application. * Some descriptions were improved. * Constructors already worked, with some caveats. Joe managed to remove those caveats, so they now work as you would expect them to. (See the RFC section for details.) * I included some references to other languages and how they handle partial application. Short version: This is the most robust and most fully featured version of any language I could find. Rock on, PHP! :-) * Nicolas, I went ahead and made a test for partial-application-DI-autowiring, just to see what it would look like. It looks like this: https://github.com/php/php-src/pull/6898/files#diff-218bb085c4cf4a35da35aa2d7a5cabd77f053d6f975340c2ef10de6f30dee503 Whether that's actually useful or not I don't know, but there it is. :-) Since there were some substantive changes, we're pushing the vote start off until the first half of next week, Monday/Tuesday timeframe. Thanks for your feedback, everyone! --Larry Garfield

Nicolas Grekas

5 years ago
Le jeu. 13 mai 2021 à 19:00, Larry Garfield <larry@garfieldtech.com> a écrit :
> On Tue, May 11, 2021, at 10:38 AM, Larry Garfield wrote: > > On Sun, Apr 25, 2021, at 2:25 PM, Larry Garfield wrote: > > > Greetings, Internalians! > > > > > > I would like to offer for your consideration another RFC, specifically > > > syntax for partial function application. > > > > > > https://wiki.php.net/rfc/partial_function_application > > > > > > It includes an implementation by Joe Watkins that is already about 95% > > > complete. (There's some edge cases he's still sorting out, but all of > > > the typical cases should work already.) Most of the design work comes > > > from Levi Morrison and Paul Crovella. I helped out with the tests, a > > > few edge bits, and general instigator/nudge. :-) > > > > > > Discuss. > > > > It looks like the conversation has died down, and it's been two weeks, > > so pending any other notable feedback I'll open a vote on this RFC on > > Thursday or Friday. > > Naturally there was more conversation. :-) But good conversation, so the > RFC has been updated again. I recommend everyone look it over another time. > > Highlights: > > * Joe was able to figure out how to fold the extra partial logic into the > Closure object. So the Partial and ReflectionPartial classes are gone; > partial application gives you a closure object that looks like any other, > but still manages to optimize away repeated partial application. > > * Some descriptions were improved. > > * Constructors already worked, with some caveats. Joe managed to remove > those caveats, so they now work as you would expect them to. (See the RFC > section for details.) > > * I included some references to other languages and how they handle > partial application. Short version: This is the most robust and most fully > featured version of any language I could find. Rock on, PHP! :-) > > * Nicolas, I went ahead and made a test for > partial-application-DI-autowiring, just to see what it would look like. It > looks like this: > > > https://github.com/php/php-src/pull/6898/files#diff-218bb085c4cf4a35da35aa2d7a5cabd77f053d6f975340c2ef10de6f30dee503 > > Whether that's actually useful or not I don't know, but there it is. :-) > > Since there were some substantive changes, we're pushing the vote start > off until the first half of next week, Monday/Tuesday timeframe. >
That's pretty damned cool, frankly. Thanks to everybody involved, I can't wait! Nicolas

Илья Сомов

5 years ago
> Greetings, Internalians! > > I would like to offer for your consideration another RFC, specifically syntax for partial function application. > > https://wiki.php.net/rfc/partial_function_application
Thank you again for one more wonderful language change! I am sure this is a huge step into the correct direction for the language. One of the nice side effects of this proposal is that it is great for reducing visual clutter when being used in functions like `array_map()`, `array_filter()` etc. However, I have a small nitpick, which may probably be a bit hard to implement: what about partially applied constructors? They are sort of functions as well. For instance: ```php $strings = [ 'value-1', 'value-2' ]; $objects = array_map( fn (string $s) => new ValueObject($s), $strings ``` to be turned into ```php $strings = [ 'value-1', 'value-2' ]; $objects = array_map(new ValueObject(?), $strings); ``` There is unfortunately no mention of partially applied constructors in the RFC. If it is not implemented as above, could you please consider adding it to the Future Scope probably?

Unnamed Person

5 years ago
On Thu, May 13, 2021 at 2:45 AM someniatko <someniatko@gmail.com> wrote:
> > > Greetings, Internalians! > > > > I would like to offer for your consideration another RFC, specifically syntax for partial function application. > > > > https://wiki.php.net/rfc/partial_function_application > > Thank you again for one more wonderful language change! I am sure this > is a huge step into the correct direction for the language. One of the > nice side effects of this proposal is that it is great for reducing > visual clutter when being used in functions like `array_map()`, > `array_filter()` etc. However, I have a small nitpick, which may > probably be a bit hard to implement: what about partially applied > constructors? They are sort of functions as well. For instance: > > ```php > $strings = [ 'value-1', 'value-2' ]; > $objects = array_map( > fn (string $s) => new ValueObject($s), > $strings > ``` > > to be turned into > ```php > $strings = [ 'value-1', 'value-2' ]; > $objects = array_map(new ValueObject(?), $strings); > ``` > > There is unfortunately no mention of partially applied constructors in > the RFC. If it is not implemented as above, could you please consider > adding it to the Future Scope probably? > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php >
The GitHub PR supports this, and there is even a comment there saying we should show this in the RFC. We'll get that updated.

Unnamed Person

5 years ago
On Thu, May 13, 2021 at 7:44 AM Levi Morrison <levi.morrison@datadoghq.com> wrote:
> > On Thu, May 13, 2021 at 2:45 AM someniatko <someniatko@gmail.com> wrote: > > > > > Greetings, Internalians! > > > > > > I would like to offer for your consideration another RFC, specifically syntax for partial function application. > > > > > > https://wiki.php.net/rfc/partial_function_application > > > > Thank you again for one more wonderful language change! I am sure this > > is a huge step into the correct direction for the language. One of the > > nice side effects of this proposal is that it is great for reducing > > visual clutter when being used in functions like `array_map()`, > > `array_filter()` etc. However, I have a small nitpick, which may > > probably be a bit hard to implement: what about partially applied > > constructors? They are sort of functions as well. For instance: > > > > ```php > > $strings = [ 'value-1', 'value-2' ]; > > $objects = array_map( > > fn (string $s) => new ValueObject($s), > > $strings > > ``` > > > > to be turned into > > ```php > > $strings = [ 'value-1', 'value-2' ]; > > $objects = array_map(new ValueObject(?), $strings); > > ``` > > > > There is unfortunately no mention of partially applied constructors in > > the RFC. If it is not implemented as above, could you please consider > > adding it to the Future Scope probably? > > > > -- > > PHP Internals - PHP Runtime Development Mailing List > > To unsubscribe, visit: https://www.php.net/unsub.php > > > > The GitHub PR supports this, and there is even a comment there saying > we should show this in the RFC. We'll get that updated.
I was a bit too hasty. In further review it does do something, but not what you'd want in situations like this. We are discussing among authors whether it is technically feasible to support the expected behavior at this time.

Илья Сомов

5 years ago
> The GitHub PR supports this, and there is even a comment there saying > we should show this in the RFC. We'll get that updated.
Thank you for the quick reaction and for your work!

Andreas Hennings

5 years ago
On Sun, 25 Apr 2021 at 21:27, Larry Garfield <larry@garfieldtech.com> wrote:
> > Greetings, Internalians! > > I would like to offer for your consideration another RFC, specifically syntax for partial function application. > > https://wiki.php.net/rfc/partial_function_application > > It includes an implementation by Joe Watkins that is already about 95% complete. (There's some edge cases he's still sorting out, but all of the typical cases should work already.) Most of the design work comes from Levi Morrison and Paul Crovella. I helped out with the tests, a few edge bits, and general instigator/nudge. :-) > > Discuss.
This is super nice! I can imagine many ways I would use it. Some questions / thoughts. ---- 1. Serializing: Will the partial function be serializable? I assume if the original function is anonymous, then neither the original nor the partial will be serializable. Also if any of the fixed parameter values is not serializable, the partial function can't be either. But outside of the above cases it should be technically possible, or not? ---- 2. var_export() Calling var_export() on a partial function could produce the original code that generated the function. E.g. var_export(foo(?), TRUE) === 'foo(?)' ----- 3. Parameter switching Could we express the following as a partial function? static function ($a, $b) {return foo($b, $a);} E.g. as foo(?1, ?0) ? Perhaps it doesn't need to be part of this RFC, but it is something to consider. ---- -- Andreas

Sara Golemon

5 years ago
On Thu, May 13, 2021 at 2:31 PM Andreas Hennings <andreas@dqxtech.net> wrote:
> 1. Serializing: > But outside of the above cases it should be technically possible, or not? > >
I suspect that the limitations you cite make any kind of general serializing both unreliable and impractical. I wouldn't plan on meaningful serialization.
> 2. var_export() > >
Same as above, really. 3. Parameter switching
> > Could we express the following as a partial function? > > static function ($a, $b) {return foo($b, $a);} > > E.g. as foo(?1, ?0) ? > >
Using named parameter we can: foo(b: ?, a: ?); -Sara

Unnamed Person

5 years ago
On Thu, May 13, 2021 at 2:31 PM Sara Golemon <pollita@php.net> wrote:
> > On Thu, May 13, 2021 at 2:31 PM Andreas Hennings <andreas@dqxtech.net> > wrote: > > > 1. Serializing: > > But outside of the above cases it should be technically possible, or not? > > > > > I suspect that the limitations you cite make any kind of general > serializing both unreliable and impractical. I wouldn't plan on meaningful > serialization. > > > > 2. var_export() > > > > > Same as above, really. > > > 3. Parameter switching > > > > Could we express the following as a partial function? > > > > static function ($a, $b) {return foo($b, $a);} > > > > E.g. as foo(?1, ?0) ? > > > > > Using named parameter we can: > > foo(b: ?, a: ?); > > -Sara
The current implementation doesn't support re-ordering anything. This feature has come up a few times. Personally, I don't think partials need to support this and every other potential feature; at some point a regular or short closure should be used. As a reminder, a `?` does not create a single argument in the closure's signature. All these `$partial`s are the same, and here I do not mean equivalent but literally the same: function f($x, $y) {} $partial = f(?); $partial = f(?, ?); $partial = f(?, ?, ?); $partial = f(?, ?, ?, ?); $partial = f(?, ?, ?, ?, ?);

Larry Garfield

5 years ago
On Thu, May 13, 2021, at 3:30 PM, Sara Golemon wrote:
> On Thu, May 13, 2021 at 2:31 PM Andreas Hennings <andreas@dqxtech.net> > wrote: > > > 1. Serializing: > > But outside of the above cases it should be technically possible, or not? > > > > > I suspect that the limitations you cite make any kind of general > serializing both unreliable and impractical. I wouldn't plan on meaningful > serialization.
Correct. A partial produces a Closure object like any other now, so it's as serializable as any other Closure object. That is to say, not at all. --Larry Garfield

Paul

5 years ago
On Sun, Apr 25, 2021 at 12:27 PM Larry Garfield <larry@garfieldtech.com> wrote:
> > Greetings, Internalians! > > I would like to offer for your consideration another RFC, specifically syntax for partial function application. > > https://wiki.php.net/rfc/partial_function_application > > It includes an implementation by Joe Watkins that is already about 95% complete. (There's some edge cases he's still sorting out, but all of the typical cases should work already.) Most of the design work comes from Levi Morrison and Paul Crovella. I helped out with the tests, a few edge bits, and general instigator/nudge. :-) > > Discuss.
Regarding "Comparison to other languages" and "Syntax choices" - I lifted the design and ? character directly from XQuery, adapting for PHP's variadic nature.

Weirdan

5 years ago
On Thu, May 13, 2021 at 10:43 PM Paul Crovella <paul.crovella@gmail.com> wrote:
> Regarding "Comparison to other languages"
Speaking of that section, there's one minor nitpick. Currently it says Perl 6 was renamed to Racket, but it was actually renamed to Raku. Racket is an unrelated, Lisp-like language.
-- Best regards, Bruce Weirdan mailto:weirdan@gmail.com

Mark Randall

5 years ago
On 25/04/2021 20:25, Larry Garfield wrote:
> Discuss.
There has been intense discussion in R11 about how the ? token applies to missing or variadic arguments. The root of the concern is that in the current proposal, ? applies as either a placeholder for a single replacement, or 0-or-more varadic arguments, depending on its position and the number of arguments. I and several others believe that this feature would be better served by restricting ? to representing a single argument, and using ...? to represent "anything else" including an empty set. ## To illustrate the scope for confusion: foo(?) looks like it requires one argument, when in reality it could accept none, 1, or a hundred. foo(1, ?) looks like it also accepts one argument, but it could accept 0, or 100, depending on the function it is wrapping. foo(?, 1) definitely requires at least one argument, but could accept more depending on what it's wrapping. ## Suggestion Based on discussions in R11 there seems to be broad agreement that ? should represent a single argument, and ...? should represent everything else (including no arguments). Thus: foo(...?) - Partial of foo with all the arguments copied over. foo(?, 1, ..?) - 1 required, then 1 fixed, 0 or more others are copied over. foo(1, ...?) - Fix the first parameter and copy over the rest. This can be seen as a mirror to the ... operator that accepts a variable number of arguments. function foo($a, ...$b) { } Equally it matches the syntax for unpacking into arguments: $a = foo($a, ...$b); Passing more arguments than the partial defines would result in an argument count error.
-- Mark Randall

Aaron Piotrowski

5 years ago
> On May 14, 2021, at 4:18 PM, Mark Randall <marandall@php.net> wrote: > > > Passing more arguments than the partial defines would result in an argument count error. >
I think it’s reasonable to allow passing more arguments to a partial since user-defined functions and closures allow this without error. Whether or not the extra arguments are automatically forwarded to the function wrapped by the partial is debatable. Consider `function foo(int $x, int $y, int $z) {}` with a partial defined as `$partial = foo(?, 42)`. If the partial is called as `$partial(73, 8)`, should 8 be forwarded to `$z` or should the call error as providing too few arguments? Or perhaps should the partial declaration should error, as it should have been `foo(?, 42, ?)` or `foo(?, 42, ...?) so the partial provided all required arguments to foo. Cheers, Aaron Piotrowski

Mark Randall

5 years ago
On 14/05/2021 22:48, Aaron Piotrowski wrote:
> I think it’s reasonable to allow passing more arguments to a partial since user-defined functions and closures allow this without error.
But only userland functions, a relic from when func_get_args was the only way to handle varaible numbers of arguments. The documentation officially discourages func_get_args in favour of ...$ so I can definitely forsee the option of us deprecating that mechanism sometime in 8.x and and removing it in 9.0. I don't think it likely that it would go the other way of allowing unlimited arguments to internal functions. That being the case, IMO it makes more sense to introduce partials with the same behaviour as internal functions, passing more functions than specified (including partial arguments) should error.

Paul

5 years ago
On Fri, May 14, 2021 at 2:49 PM Aaron Piotrowski <aaron@trowski.com> wrote:
> > Consider `function foo(int $x, int $y, int $z) {}` with a partial defined as `$partial = foo(?, 42)`. > > If the partial is called as `$partial(73, 8)`, should 8 be forwarded to `$z` or should the call error as providing too few arguments?
The 8 passes along to $z. There is no error, all required arguments have been provided.
> Or perhaps should the partial declaration should error, as it should have been `foo(?, 42, ?)` or `foo(?, 42, ...?) so the partial provided all required arguments to foo.
I think this highlights where the misunderstanding of this feature is. Partial application is about binding arguments. ? isn't an argument, it's an argument placeholder. It does two things: signals to create a closure wrapping the function rather than calling it immediately, and holds a position in the argument list so that an argument further to the right can be fixed (bound) at that time. Arguments are bound; argument placeholders are not, they exist only for convenience. The syntax `foo(?, 42)` doesn't call foo, let alone provide any arguments to it, it simply creates a closure that'll pass along 42 at the appropriate argument position along with whatever else it's provided with. Requiring additional trailing argument placeholders or adding an additional token `...?` unnecessarily complicates things, burdens the user, and only serves to further promote misunderstanding.

Aaron Piotrowski

5 years ago
> On May 14, 2021, at 6:09 PM, Paul Crovella <paul.crovella@gmail.com> wrote: > > On Fri, May 14, 2021 at 2:49 PM Aaron Piotrowski <aaron@trowski.com> wrote: >> >> Consider `function foo(int $x, int $y, int $z) {}` with a partial defined as `$partial = foo(?, 42)`. >> >> If the partial is called as `$partial(73, 8)`, should 8 be forwarded to `$z` or should the call error as providing too few arguments? > > The 8 passes along to $z. There is no error, all required arguments > have been provided.
In the current proposal, yes. In a hypothetical implementation where ? represented a single argument, I was asking what made sense. In that situation, I think 8 passing along still makes sense.
> >> Or perhaps should the partial declaration should error, as it should have been `foo(?, 42, ?)` or `foo(?, 42, ...?) so the partial provided all required arguments to foo. > > I think this highlights where the misunderstanding of this feature is. > Partial application is about binding arguments. ? isn't an argument, > it's an argument placeholder. It does two things: signals to create a > closure wrapping the function rather than calling it immediately, and > holds a position in the argument list so that an argument further to > the right can be fixed (bound) at that time. Arguments are bound; > argument placeholders are not, they exist only for convenience. The > syntax `foo(?, 42)` doesn't call foo, let alone provide any arguments > to it, it simply creates a closure that'll pass along 42 at the > appropriate argument position along with whatever else it's provided > with. > > Requiring additional trailing argument placeholders or adding an > additional token `...?` unnecessarily complicates things, burdens the > user, and only serves to further promote misunderstanding. >
My issue is the dual-meaning of ? in the current proposal. In `foo(?, 42)`, the ? represents a single argument, but adding a trailing ? (such as in `foo(?, 42, ?)`) represents any number of arguments. Would it perhaps make sense to make superfluous ? markers an error? foo(?); // Fine, needed to define a partial with no bound args. foo(?, 42); // Ok, binds second arg. foo(?, ?, 42); // Ok, binds third arg. foo(?, 42, ?); // Error, unnecessary placeholder. foo(?, ?); // Error, unnecessary placeholder. The intention here is to keep the syntax unambiguous. foo(?) == foo(?, ?) == foo(?, ?, ?) and so forth is not going to be obvious to everyone, so why allow meaningless and misleading syntax. Cheers, Aaron Piotrowski

Larry Garfield

5 years ago
Mark Randall:
> Based on discussions in R11 there seems to be broad agreement that ? > should represent a single argument, and ...? should represent everything > else (including no arguments).
This is incorrect. There were a few people arguing for that, but there was far from a consensus on that point. None of the RFC authors were convinced of the arguments given, for example. On Fri, May 14, 2021, at 6:44 PM, Aaron Piotrowski wrote:
> > > On May 14, 2021, at 6:09 PM, Paul Crovella <paul.crovella@gmail.com> wrote:
> >> Or perhaps should the partial declaration should error, as it should have been `foo(?, 42, ?)` or `foo(?, 42, ...?) so the partial provided all required arguments to foo. > > > > I think this highlights where the misunderstanding of this feature is. > > Partial application is about binding arguments. ? isn't an argument, > > it's an argument placeholder. It does two things: signals to create a > > closure wrapping the function rather than calling it immediately, and > > holds a position in the argument list so that an argument further to > > the right can be fixed (bound) at that time. Arguments are bound; > > argument placeholders are not, they exist only for convenience. The > > syntax `foo(?, 42)` doesn't call foo, let alone provide any arguments > > to it, it simply creates a closure that'll pass along 42 at the > > appropriate argument position along with whatever else it's provided > > with. > > > > Requiring additional trailing argument placeholders or adding an > > additional token `...?` unnecessarily complicates things, burdens the > > user, and only serves to further promote misunderstanding. > > My issue is the dual-meaning of ? in the current proposal. In `foo(?, > 42)`, the ? represents a single argument, but adding a trailing ? (such > as in `foo(?, 42, ?)`) represents any number of arguments. Would it > perhaps make sense to make superfluous ? markers an error? > > foo(?); // Fine, needed to define a partial with no bound args. > foo(?, 42); // Ok, binds second arg. > foo(?, ?, 42); // Ok, binds third arg. > foo(?, 42, ?); // Error, unnecessary placeholder. > foo(?, ?); // Error, unnecessary placeholder. > > The intention here is to keep the syntax unambiguous. > > foo(?) == foo(?, ?) == foo(?, ?, ?) and so forth is not going to be > obvious to everyone, so why allow meaningless and misleading syntax.
Is that actually going to come up? Given that PHP functions (at least user-space ones) accept extra trailing arguments and just let them fall off, I would *expect* a closure that way to do the same. Named arguments continue that, I believe, by just ignoring any variadic arguments that do not match a parameter in the function. It seems odd to go back on that behavior now. I can't speak for the others, but I could tolerate making "more than one extra ? beyond the end of the parameter list is an error", potentially, as at that point they're redundant. But if a function has, say, 4 params, then fourParams(1, 3, ?) is a convenient way to say "and placeholder everything else". Especially in dynamic cases like Nicolas pointed out, you may not necessarily know how many arguments there are. As Paul noted above, this isn't a syntax for calling a function; it's effectively an even-shorter-hand way to write a short lambda. The rest of the closure construction is derived from the function being partially applied. --Larry Garfield

Mark Randall

5 years ago
On 15/05/2021 01:00, Larry Garfield wrote:
> I can't speak for the others, but I could tolerate making "more than one extra ? beyond the end of the parameter list is an error", potentially, as at that point they're redundant. But if a function has, say, 4 params, then fourParams(1, 3, ?) is a convenient way to say "and placeholder everything else".
Fortunately, we already have an existing and recognised way of saying "and everything else" https://www.php.net/manua/en/functions.arguments.php#functions.variablearg-list

Aaron Piotrowski

5 years ago
> On May 14, 2021, at 7:00 PM, Larry Garfield <larry@garfieldtech.com> wrote: > > Is that actually going to come up? Given that PHP functions (at least user-space ones) accept extra trailing arguments and just let them fall off, I would *expect* a closure that way to do the same. Named arguments continue that, I believe, by just ignoring any variadic arguments that do not match a parameter in the function. It seems odd to go back on that behavior now.
I don't consider forwarding extra arguments an issue. I briefly was thinking it might be nice to be explicit about the number of arguments a partial would accept, but you convinced me otherwise in R11, so I think we're on the same page here.
> > I can't speak for the others, but I could tolerate making "more than one extra ? beyond the end of the parameter list is an error", potentially, as at that point they're redundant. But if a function has, say, 4 params, then fourParams(1, 3, ?) is a convenient way to say "and placeholder everything else". Especially in dynamic cases like Nicolas pointed out, you may not necessarily know how many arguments there are.
With what I proposed in my last email, `fourParams(1, 3, ?)` is acceptable, there's nothing superfluous there. At least one ? is needed to declare a partial. Similarly, a partial for a no parameter function: `$partial = functionTakingNoParams(?)`. Or even a partial with args bound to all four params: `fourParams(1, 2, 3, 4, ?)`. What would error is `fourParams(1, 3, ?, ?)`, as the second ? is meaningless. I think you've convinced me that one-for-one matching on ? is burdensome, but the above is a happy medium perhaps? Cheers, Aaron Piotrowski

Larry Garfield

5 years ago
On Fri, May 14, 2021, at 7:20 PM, Aaron Piotrowski wrote:
> > > On May 14, 2021, at 7:00 PM, Larry Garfield <larry@garfieldtech.com> wrote: > > > > Is that actually going to come up? Given that PHP functions (at least user-space ones) accept extra trailing arguments and just let them fall off, I would *expect* a closure that way to do the same. Named arguments continue that, I believe, by just ignoring any variadic arguments that do not match a parameter in the function. It seems odd to go back on that behavior now. > > I don't consider forwarding extra arguments an issue. I briefly was > thinking it might be nice to be explicit about the number of arguments > a partial would accept, but you convinced me otherwise in R11, so I > think we're on the same page here. > > > > > I can't speak for the others, but I could tolerate making "more than one extra ? beyond the end of the parameter list is an error", potentially, as at that point they're redundant. But if a function has, say, 4 params, then fourParams(1, 3, ?) is a convenient way to say "and placeholder everything else". Especially in dynamic cases like Nicolas pointed out, you may not necessarily know how many arguments there are. > > With what I proposed in my last email, `fourParams(1, 3, ?)` is > acceptable, there's nothing superfluous there. At least one ? is needed > to declare a partial. Similarly, a partial for a no parameter function: > `$partial = functionTakingNoParams(?)`. Or even a partial with args > bound to all four params: `fourParams(1, 2, 3, 4, ?)`. > > What would error is `fourParams(1, 3, ?, ?)`, as the second ? is meaningless. > > I think you've convinced me that one-for-one matching on ? is > burdensome, but the above is a happy medium perhaps?
I'd be OK with "no more than one trailing ? in excess of what the underlying callable has." (Which means if you don't know, just stick one ? at the end and you know it will work.) --Larry Garfield

Aaron Piotrowski

5 years ago
On May 14, 2021, at 7:36 PM, Larry Garfield <larry@garfieldtech.com> wrote:
> > On Fri, May 14, 2021, at 7:20 PM, Aaron Piotrowski wrote: >> >>> On May 14, 2021, at 7:00 PM, Larry Garfield <larry@garfieldtech.com> wrote: >>> >>> Is that actually going to come up? Given that PHP functions (at least user-space ones) accept extra trailing arguments and just let them fall off, I would *expect* a closure that way to do the same. Named arguments continue that, I believe, by just ignoring any variadic arguments that do not match a parameter in the function. It seems odd to go back on that behavior now. >> >> I don't consider forwarding extra arguments an issue. I briefly was >> thinking it might be nice to be explicit about the number of arguments >> a partial would accept, but you convinced me otherwise in R11, so I >> think we're on the same page here. >> >>> >>> I can't speak for the others, but I could tolerate making "more than one extra ? beyond the end of the parameter list is an error", potentially, as at that point they're redundant. But if a function has, say, 4 params, then fourParams(1, 3, ?) is a convenient way to say "and placeholder everything else". Especially in dynamic cases like Nicolas pointed out, you may not necessarily know how many arguments there are. >> >> With what I proposed in my last email, `fourParams(1, 3, ?)` is >> acceptable, there's nothing superfluous there. At least one ? is needed >> to declare a partial. Similarly, a partial for a no parameter function: >> `$partial = functionTakingNoParams(?)`. Or even a partial with args >> bound to all four params: `fourParams(1, 2, 3, 4, ?)`. >> >> What would error is `fourParams(1, 3, ?, ?)`, as the second ? is meaningless. >> >> I think you've convinced me that one-for-one matching on ? is >> burdensome, but the above is a happy medium perhaps? > > I'd be OK with "no more than one trailing ? in excess of what the underlying callable has." (Which means if you don't know, just stick one ? at the end and you know it will work.)
I think multiple trailing ? should be an error, otherwise how am I suppose to know at a glance if a partial declaration will error? Plus it’s adding multiple ways to declare the same thing, which I was hoping to avoid. fourParams(1, 2, ?); // OK fourParams(1, 2, ?, ?); // OK for you, should error to me fourParams(1, 2, ?, ?, ?); // Again OK for you, should error to me fourParams(1, 2, ?, ?, ?, ?); // Error for both What value is gained in allowing any of those but the first? I’d also be fine allowing a trailing ? in any declaration. It’s unnecessary, but one could argue that it’s consistent to allow a trailing ? in any partial, since it’s required for some. fourParams(?, 2, ?); // Could error, but probably fine for consistency Aaron Piotrowski

Paul

5 years ago
On Fri, May 14, 2021 at 4:44 PM Aaron Piotrowski <aaron@trowski.com> wrote:
> > My issue is the dual-meaning of ? in the current proposal. In `foo(?, 42)`, the ? represents a single argument, but adding a trailing ? (such as in `foo(?, 42, ?)`) represents any number of arguments. Would it perhaps make sense to make superfluous ? markers an error? > > foo(?); // Fine, needed to define a partial with no bound args. > foo(?, 42); // Ok, binds second arg. > foo(?, ?, 42); // Ok, binds third arg. > foo(?, 42, ?); // Error, unnecessary placeholder. > foo(?, ?); // Error, unnecessary placeholder. > > The intention here is to keep the syntax unambiguous. > > foo(?) == foo(?, ?) == foo(?, ?, ?) and so forth is not going to be obvious to everyone, so why allow meaningless and misleading syntax. > > Cheers, > Aaron Piotrowski >
While it's my preference not to use superfluous placeholders they do no real harm and I do not feel comfortable imposing this preference on others.

Larry Garfield

5 years ago
On Mon, May 17, 2021, at 11:14 PM, Paul Crovella wrote:
> On Fri, May 14, 2021 at 4:44 PM Aaron Piotrowski <aaron@trowski.com> wrote: > > > > My issue is the dual-meaning of ? in the current proposal. In `foo(?, 42)`, the ? represents a single argument, but adding a trailing ? (such as in `foo(?, 42, ?)`) represents any number of arguments. Would it perhaps make sense to make superfluous ? markers an error? > > > > foo(?); // Fine, needed to define a partial with no bound args. > > foo(?, 42); // Ok, binds second arg. > > foo(?, ?, 42); // Ok, binds third arg. > > foo(?, 42, ?); // Error, unnecessary placeholder. > > foo(?, ?); // Error, unnecessary placeholder. > > > > The intention here is to keep the syntax unambiguous. > > > > foo(?) == foo(?, ?) == foo(?, ?, ?) and so forth is not going to be obvious to everyone, so why allow meaningless and misleading syntax. > > > > Cheers, > > Aaron Piotrowski > > > > While it's my preference not to use superfluous placeholders they do > no real harm and I do not feel comfortable imposing this preference on > others.
User-space functions have always accepted more arguments than they're defined with. They just get dropped off the end silently, unless you use func_get_args() or variadics. While I know not everyone likes that "feature", it means that extra trailing ? "arguments" don't feel weird to me. They just get dropped off the end and we move on with life. At least that's how I conceptualize them. --Larry Garfield

David Gebler

5 years ago
On Tue, May 18, 2021 at 2:45 PM Larry Garfield <larry@garfieldtech.com> wrote:
> User-space functions have always accepted more arguments than they're > defined with. They just get dropped off the end silently, unless you use > func_get_args() or variadics. While I know not everyone likes that > "feature", it means that extra trailing ? "arguments" don't feel weird to > me. They just get dropped off the end and we move on with life. At least > that's how I conceptualize them. >
This is my question about the partials feature, though; does the implementation have potential to reasonably be confusing? if it's essentially a convenient way of creating a closure, why would extra arguments passed in the closure call be passed to the wrapped function, rather than discarded? function foo(int $a, int $b, int ...$p) { ... } $partial = foo(?, 10); $partial(5, 15, 25); Intuitively, because the existing convention is extra unused parameters in user defined functions are silently ignored, I think I would expect the above to be equivalent to something like: $partial = fn(int $a) => foo($a, 10); $partial(5, 15, 25); // 15 and 25 are lopped off to no effect and not $partial = fn(int $a, int ...$params) => foo($a, 10, ....$params); But correct me if I'm wrong, isn't the latter what the RFC effectively proposes?

Rowan Collins

5 years ago
Hi David, On 18/05/2021 20:56, David Gebler wrote:
> function foo(int $a, int $b, int ...$p) { ... } > $partial = foo(?, 10); > > $partial(5, 15, 25); > > Intuitively, because the existing convention is extra unused parameters in > user defined functions are silently ignored, I think I would expect the > above to be equivalent to something like: > > $partial = fn(int $a) => foo($a, 10); > $partial(5, 15, 25); // 15 and 25 are lopped off to no effect > > and not > > $partial = fn(int $a, int ...$params) => foo($a, 10, ....$params);
Did you see my message yesterday about the two mental models of the feature? https://externals.io/message/114157#114492 Your expectation there is in line with the "start with an empty closure and add things" model; the behaviour proposed in the RFC is in line with the "copy the full signature and then splice in fixed values" model. I do worry that users of the language will assume the "wrong" mental model, though, unless we pick a syntax that more clearly matches the "copy and splice" model. I think that would mean having a way to say "make this is a partial", and a way to indicate which arguments to "splice", without any "placeholders". Yet Another Bad Unsolicited Syntax Suggestion: $partial = partial foo(); // no bindings; current RFC $partial = foo(?); $partial = partial foo(b: 10); // bind named parameter $b; current RFC $partial = foo(?, b: 10); $partial = partial foo(1: 10); // bind positional parameter #1 (zero-indexed); current RFC $partial = foo(?, 10); Regards,
-- Rowan Tommins [IMSoP]

David Gebler

5 years ago
On Tue, May 18, 2021 at 9:51 PM Rowan Tommins <rowan.collins@gmail.com> wrote:
> Hi David, > > Did you see my message yesterday about the two mental models of the > feature? https://externals.io/message/114157#114492 > > Your expectation there is in line with the "start with an empty closure > and add things" model; the behaviour proposed in the RFC is in line with > the "copy the full signature and then splice in fixed values" model. >
Well this is the crux of the issue for me, yes; I think the latter model is arguably more useful, I think the former model is what the proposed syntax implies and the former model is therefore the more natural and "readable" interpretation of what users might expect partials to mean when we're looking at hypothetical userland code built on this feature. I can't help but question the value of introducing something which has the potential to be ambiguous and misinterpreted - and I think the discussion in this thread has highlighted that there *is* ambiguity. Further, the RFC as it stands seems to introduce the possibility of writing ambiguous code when defining a partial. My intuition is that most PHP users would expect a single placeholder character (?) to represent one and exactly one parameter, analogous to SQL. But there are other ways you might reasonably interpret and argue for the syntax, which I think have been well-expressed by Larry and others. I don't know what the answer is here, I'm reading through the entire thread and RFC again and trying to better formulate in my mind how I'd [like to] reason about this in terms of syntax. But the more I think about it, the more I realise much as I loved the idea when I first read the RFC, now I'm worried we're rushing to include something (because there's an implementation, more or less ready to go) which maybe should go back to the drawing board for a bit of a re-think. Maybe I'm wrong, I don't know, but yeah...I have reservations now. If I had a vote and voting was open at this point, I think my inclination would be to -1 the RFC as it currently stands, not because it isn't a good idea but because right now, it will definitely confuse some significant proportion of users.

Unnamed Person

5 years ago
> I don't know what the answer is here, I'm reading through the entire thread > and RFC again and trying to better formulate in my mind how I'd [like to] > reason about this in terms of syntax. But the more I think about it, the > more I realise much as I loved the idea when I first read the RFC, now I'm > worried we're rushing to include something (because there's an > implementation, more or less ready to go) which maybe should go back to the > drawing board for a bit of a re-think.
Some of the RFC authors have been discussing this a lot based on feedback. In a [previous message][1] I said we would message the list when it's ready for re-review. It's still not ready, but we seem to be driving towards consensus. I wish everyone would hold the discussion until then because they are discussing an outdated thing and causing noise, but it's not something I can control. [1]: https://externals.io/message/114157#114500

Hossein Baghayi

5 years ago
> Requiring additional trailing argument placeholders or adding an > additional token `...?` unnecessarily complicates things, burdens the > user, and only serves to further promote misunderstanding. > > Providing ? as a means of placeholder for some arguments and ignoring the
rest could complicate the readability in my opinion. Maybe we should move (?) out of the arguments list as a means of creating a partial. What I realized is that we need a way of signaling partial function creation. Be it foo(?) or foo(?, ?, ?) or however many ? is added there, which does not convey any meaning and also doesn't produce any sort of error! Say instead of foo(?) we had ?foo(). Since we have named parameters it could help us in providing some of the arguments and deferring the rest. For instance: ``` function foo($x, $y, ...$z) {} ?foo(); // causes a partial ?foo(y: '..'); // also causes a partial ``` This way we wouldn't need to worry about the number of ? added to arguments list. It may also help in avoiding future PSRs in telling us how many ? we should put in there :)

Hossein Baghayi

5 years ago
On Sat, 15 May 2021 at 09:03, Hossein Baghayi <hossein.baghayi@gmail.com> wrote:
> Providing ? as a means of placeholder for some arguments and ignoring the > rest could complicate the readability in my opinion. > Maybe we should move (?) out of the arguments list as a means of creating > a partial. > > What I realized is that we need a way of signaling partial function > creation. Be it foo(?) or foo(?, ?, ?) or however many ? is added there, > which does not convey any meaning and also doesn't produce any sort of > error! > > Say instead of foo(?) we had ?foo(). > Since we have named parameters it could help us in providing some of the > arguments and deferring the rest. > > For instance: > ``` > function foo($x, $y, ...$z) {} > > ?foo(); // causes a partial > ?foo(y: '..'); // also causes a partial > ``` > > This way we wouldn't need to worry about the number of ? added to > arguments list. > It may also help in avoiding future PSRs in telling us how many ? we > should put in there :) >
In addition to these, I was thinking of 2 other cases in which changing the current proposal might be helpful. 1- When there is a parameterless function (an expensive operation maybe). 2- When all parameters are passed but the function is not expected to be called yet. In the case of a parameterless function, maybe it is an expensive function call and we need to defer calling it. Maybe all we need is to hold a reference to it and pass it around? With the current proposal, I do not know if it is possible or not. Since there are no parameters defined. ``` function with_expensive_operations_lurking_inside() {...} ```` Can we or should we call this function this way: ``` $ref = with_expensive_operations_lurking_inside(?); ``` It feels odd having to provide parameters when there is none needed. For the other use case where all parameters are passed but is not expected to be called yet: Maybe providing parameters and setting it up is not our responsibility. ``` function expensive_or_not($a, $b, $c) {...} $do_not_get_called_please = expensive_or_not(1, 2, 3, ?); // with an extra parameter as to mark as partial! $do_not_get_called_please = expensive_or_not(?, 1, 2, 3); // or maybe this way?! ``` Well, I was thinking that by changing the proposed syntax we could achieve what is proposed and a little bit more. Also we wouldn't need to worry about the number of ? needed as arguments. Since all we need is to mark the return type as partial (closure) on the fly and grab hold of what is passed as arguments. There are some different syntaxes that come to my mind: We could still use ? but outside of the arguments list? ``` $partial = xyx?(..); $partial = ?xyx(..); ``` or maybe different symbols: ``` $partial = :xyz(..); ``` We might be able to even cast the return type: ``` $partial = (?) xyz(..); $partial = (partial) xyz(..); $partial = (fn) xyz(..); ```

Mike Schinkel

5 years ago
> On May 16, 2021, at 10:43 PM, Hossein Baghayi <hossein.baghayi@gmail.com> wrote: > > On Sat, 15 May 2021 at 09:03, Hossein Baghayi <hossein.baghayi@gmail.com> > wrote: > >> Providing ? as a means of placeholder for some arguments and ignoring the >> rest could complicate the readability in my opinion. >> Maybe we should move (?) out of the arguments list as a means of creating >> a partial. >> >> What I realized is that we need a way of signaling partial function >> creation. Be it foo(?) or foo(?, ?, ?) or however many ? is added there, >> which does not convey any meaning and also doesn't produce any sort of >> error! >> >> Say instead of foo(?) we had ?foo(). >> Since we have named parameters it could help us in providing some of the >> arguments and deferring the rest. >> >> For instance: >> ``` >> function foo($x, $y, ...$z) {} >> >> ?foo(); // causes a partial >> ?foo(y: '..'); // also causes a partial >> ``` >> >> This way we wouldn't need to worry about the number of ? added to >> arguments list. >> It may also help in avoiding future PSRs in telling us how many ? we >> should put in there :) >> > > In addition to these, I was thinking of 2 other cases in which changing the > current proposal might be helpful. > > 1- When there is a parameterless function (an expensive operation maybe). > 2- When all parameters are passed but the function is not expected to be > called yet. > > In the case of a parameterless function, maybe it is an expensive function > call and we need to defer calling it. > Maybe all we need is to hold a reference to it and pass it around? > With the current proposal, I do not know if it is possible or not. Since > there are no parameters defined. > ``` > function with_expensive_operations_lurking_inside() {...} > ```` > Can we or should we call this function this way: > ``` > $ref = with_expensive_operations_lurking_inside(?); > ``` > It feels odd having to provide parameters when there is none needed. > > > For the other use case where all parameters are passed but is not expected > to be called yet: > Maybe providing parameters and setting it up is not our responsibility. > ``` > function expensive_or_not($a, $b, $c) {...} > $do_not_get_called_please = expensive_or_not(1, 2, 3, ?); // with an extra > parameter as to mark as partial! > $do_not_get_called_please = expensive_or_not(?, 1, 2, 3); // or maybe this > way?! > ``` > > Well, I was thinking that by changing the proposed syntax we could achieve > what is proposed and a little bit more. > Also we wouldn't need to worry about the number of ? needed as arguments. > Since all we need is to mark the return type as partial (closure) on the > fly and grab hold of what is passed as arguments. > > There are some different syntaxes that come to my mind: > We could still use ? but outside of the arguments list? > ``` > $partial = xyx?(..); > $partial = ?xyx(..); > ``` > or maybe different symbols: > ``` > $partial = :xyz(..); > ``` > > We might be able to even cast the return type: > ``` > $partial = (?) xyz(..); > $partial = (partial) xyz(..); > $partial = (fn) xyz(..); > ```
Casting is another interesting approach that does feel more consistent with the existing language. Since it *is* creating a closure, wouldn't this make the most sense? $partial = (closure) abc(); $partial = (closure) xyz(?,24); -Mike

Guilliam Xavier

5 years ago
On Mon, May 17, 2021 at 6:58 AM Mike Schinkel <mike@newclarity.net> wrote:
> > > On May 16, 2021, at 10:43 PM, Hossein Baghayi <hossein.baghayi@gmail.com> > wrote: > > > > On Sat, 15 May 2021 at 09:03, Hossein Baghayi <hossein.baghayi@gmail.com > > > > wrote: > > > >> Providing ? as a means of placeholder for some arguments and ignoring > the > >> rest could complicate the readability in my opinion. > >> Maybe we should move (?) out of the arguments list as a means of > creating > >> a partial. > >> > >> What I realized is that we need a way of signaling partial function > >> creation. Be it foo(?) or foo(?, ?, ?) or however many ? is added there, > >> which does not convey any meaning and also doesn't produce any sort of > >> error! > >> > >> Say instead of foo(?) we had ?foo(). > >> Since we have named parameters it could help us in providing some of the > >> arguments and deferring the rest. > >> > >> For instance: > >> ``` > >> function foo($x, $y, ...$z) {} > >> > >> ?foo(); // causes a partial > >> ?foo(y: '..'); // also causes a partial > >> ``` > >> > >> This way we wouldn't need to worry about the number of ? added to > >> arguments list. > >> It may also help in avoiding future PSRs in telling us how many ? we > >> should put in there :) > >> > > > > In addition to these, I was thinking of 2 other cases in which changing > the > > current proposal might be helpful. > > > > 1- When there is a parameterless function (an expensive operation maybe). > > 2- When all parameters are passed but the function is not expected to be > > called yet. > > > > In the case of a parameterless function, maybe it is an expensive > function > > call and we need to defer calling it. > > Maybe all we need is to hold a reference to it and pass it around? > > With the current proposal, I do not know if it is possible or not. Since > > there are no parameters defined. > > ``` > > function with_expensive_operations_lurking_inside() {...} > > ```` > > Can we or should we call this function this way: > > ``` > > $ref = with_expensive_operations_lurking_inside(?); > > ``` > > It feels odd having to provide parameters when there is none needed. > > > > > > For the other use case where all parameters are passed but is not > expected > > to be called yet: > > Maybe providing parameters and setting it up is not our responsibility. > > ``` > > function expensive_or_not($a, $b, $c) {...} > > $do_not_get_called_please = expensive_or_not(1, 2, 3, ?); // with an > extra > > parameter as to mark as partial! > > $do_not_get_called_please = expensive_or_not(?, 1, 2, 3); // or maybe > this > > way?! > > ``` > > > > Well, I was thinking that by changing the proposed syntax we could > achieve > > what is proposed and a little bit more. > > Also we wouldn't need to worry about the number of ? needed as arguments. > > Since all we need is to mark the return type as partial (closure) on the > > fly and grab hold of what is passed as arguments. > > > > There are some different syntaxes that come to my mind: > > We could still use ? but outside of the arguments list? > > ``` > > $partial = xyx?(..); > > $partial = ?xyx(..); > > ``` > > or maybe different symbols: > > ``` > > $partial = :xyz(..); > > ``` > > > > We might be able to even cast the return type: > > ``` > > $partial = (?) xyz(..); > > $partial = (partial) xyz(..); > > $partial = (fn) xyz(..); > > ``` > > Casting is another interesting approach that does feel more consistent > with the existing language. > > Since it *is* creating a closure, wouldn't this make the most sense? > > $partial = (closure) abc(); >
Ouch! definitely NOT!
> $partial = (closure) xyz(?,24); >
> -Mike > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > >
But I agree that these two cases "don't feel quite right": ``` function noParam() {/*...*/} $p = noParam(?); /* looks like there's 1 param, to be passed on call */ ``` ``` function threeParams($a, $b, $c) {/*...*/} $p = threeParams(1, 2, 3, ?); /* looks like there's 4 params, with the last one to be passed on call */ ``` (both would be called as `$p()`, i.e. without any arg). Here we had to add a `?` only for "technical" reasons. More generally I also agree that `?` having two different meanings/behaviors in e.g. `f(?, 2)` [or `f(1, ?, 3)`] vs `f(1, ?)` -- namely: "exactly 1 arg" vs "0+ args" -- is confusing. I think I too would prefer `?` to only mean "exactly 1 arg", and - either have another token for "0+ args" (e.g.: `f(?, 2)` [and `f(1, ?, 3)`] vs `f(1, ...)`, and also `noParam(...)` and `threeParams(1, 2, 3, ...)` ) - or have another syntax like Hossein first suggested (e.g.: `*f(?, 2)` [and `*f(1, ?, 3)`] vs `*f(1)`, and also `*noParam()` and `*threeParams(1, 2, 3)`). Unless there are compelling (or at least convincing) reasons against? Thanks,
-- Guilliam Xavier

Unnamed Person

5 years ago
Joe Watkins has successfully worked out some bugs that also enable more powerful behavior. Thanks to Nikita and any others who worked with Joe on fixing these issues and reviewing the PR. The short of it is that re-ordering parameters when using named placeholders is now technically possible. We will send an update when the RFC and implementation are ready for re-review.

Guilliam Xavier

5 years ago
On Mon, May 17, 2021 at 5:01 PM Levi Morrison <levi.morrison@datadoghq.com> wrote:
> Joe Watkins has successfully worked out some bugs that also enable > more powerful behavior. Thanks to Nikita and any others who worked > with Joe on fixing these issues and reviewing the PR. > > The short of it is that re-ordering parameters when using named > placeholders is now technically possible. We will send an update when > the RFC and implementation are ready for re-review. >
That's great news! By the way, I forgot to add that another syntax might also be clearer when binding a value to a named parameter, e.g.: ``` function f($a, $b, $c, $d, $e) {/*...*/} /* We want to bind value 4 to param "d" */ // with current syntax: $p = f(?, d: 4); /* or f(?, ?, ?, 4) */ // with another hypothetical syntax: $p = *f(d: 4); ``` Anyway, looking forward to your update =) Thanks,
-- Guilliam Xavier

Alexandru Pătrănescu

5 years ago
On Mon, May 17, 2021 at 6:36 PM Guilliam Xavier <guilliam.xavier@gmail.com> wrote:
> On Mon, May 17, 2021 at 5:01 PM Levi Morrison <levi.morrison@datadoghq.com > > > wrote: > > > Joe Watkins has successfully worked out some bugs that also enable > > more powerful behavior. Thanks to Nikita and any others who worked > > with Joe on fixing these issues and reviewing the PR. > > > > The short of it is that re-ordering parameters when using named > > placeholders is now technically possible. We will send an update when > > the RFC and implementation are ready for re-review. > > > > That's great news! > > By the way, I forgot to add that another syntax might also be clearer when > binding a value to a named parameter, e.g.: > > ``` > function f($a, $b, $c, $d, $e) {/*...*/} > > /* We want to bind value 4 to param "d" */ > > // with current syntax: > $p = f(?, d: 4); /* or f(?, ?, ?, 4) */ > > // with another hypothetical syntax: > $p = *f(d: 4); > ``` > > Anyway, looking forward to your update =) > > Thanks, > > -- > Guilliam Xavier >
Hey Guilliam, adding a special character like * or ? before the function name is not going to work. It might look good for *f() but it has the same issues as casting. You should be able to have with the current version a code like $actionGenerator->getDriveAction()('home', ?) You can't really put the * or ? before the function name here. Maybe having it just before the parenthesis would work. Regards, Alex

Mike Schinkel

5 years ago
> On May 17, 2021, at 10:50 AM, Guilliam Xavier <guilliam.xavier@gmail.com> wrote: > > > > On Mon, May 17, 2021 at 6:58 AM Mike Schinkel <mike@newclarity.net <mailto:mike@newclarity.net>> wrote: > > > > Well, I was thinking that by changing the proposed syntax we could achieve > > what is proposed and a little bit more. > > Also we wouldn't need to worry about the number of ? needed as arguments. > > Since all we need is to mark the return type as partial (closure) on the > > fly and grab hold of what is passed as arguments. > > > > There are some different syntaxes that come to my mind: > > We could still use ? but outside of the arguments list? > > ``` > > $partial = xyx?(..); > > $partial = ?xyx(..); > > ``` > > or maybe different symbols: > > ``` > > $partial = :xyz(..); > > ``` > > > > We might be able to even cast the return type: > > ``` > > $partial = (?) xyz(..); > > $partial = (partial) xyz(..); > > $partial = (fn) xyz(..); > > ``` > > Casting is another interesting approach that does feel more consistent with the existing language. > > Since it *is* creating a closure, wouldn't this make the most sense? > > $partial = (closure) abc(); > > Ouch! definitely NOT! > > $partial = (closure) xyz(?,24);
Mind if I ask for you to elaborate on your aversion? Asking for general understanding, not to debate the point. -Mike

Alexandru Pătrănescu

5 years ago
On Mon, May 17, 2021 at 6:16 PM Mike Schinkel <mike@newclarity.net> wrote:
> > > > On May 17, 2021, at 10:50 AM, Guilliam Xavier <guilliam.xavier@gmail.com> > wrote: > > > > > > > > On Mon, May 17, 2021 at 6:58 AM Mike Schinkel <mike@newclarity.net > <mailto:mike@newclarity.net>> wrote: > > > > Casting is another interesting approach that does feel more consistent > with the existing language. > > > > Since it *is* creating a closure, wouldn't this make the most sense? > > > > $partial = (closure) abc(); > > > > Ouch! definitely NOT! > > > > $partial = (closure) xyz(?,24); > > > Mind if I ask for you to elaborate on your aversion? > > Asking for general understanding, not to debate the point. > > -Mike
Hey Mike, Let me share something that's possible now and it would be pretty weird with the casting syntax: *(function (...$params) { var_dump($params);})(?, 2)(1, ?)(?, 4)(?, ?, 6)(?, 5)(3, ?)(7);* Will pass the parameter 1, 2, 3, 4, 5, 6, 7 in order to the closure that was initially defined. I think casting requires extra grouping.

Guilliam Xavier

5 years ago
On Mon, May 17, 2021 at 5:16 PM Mike Schinkel <mike@newclarity.net> wrote:
> > > On May 17, 2021, at 10:50 AM, Guilliam Xavier <guilliam.xavier@gmail.com> > wrote: > > > > On Mon, May 17, 2021 at 6:58 AM Mike Schinkel <mike@newclarity.net> wrote: > >> > >> > Well, I was thinking that by changing the proposed syntax we could >> achieve >> > what is proposed and a little bit more. >> > Also we wouldn't need to worry about the number of ? needed as >> arguments. >> > Since all we need is to mark the return type as partial (closure) on the >> > fly and grab hold of what is passed as arguments. >> > >> > There are some different syntaxes that come to my mind: >> > We could still use ? but outside of the arguments list? >> > ``` >> > $partial = xyx?(..); >> > $partial = ?xyx(..); >> > ``` >> > or maybe different symbols: >> > ``` >> > $partial = :xyz(..); >> > ``` >> > >> > We might be able to even cast the return type: >> > ``` >> > $partial = (?) xyz(..); >> > $partial = (partial) xyz(..); >> > $partial = (fn) xyz(..); >> > ``` >> >> Casting is another interesting approach that does feel more consistent >> with the existing language. >> >> Since it *is* creating a closure, wouldn't this make the most sense? >> >> $partial = (closure) abc(); >> > > Ouch! definitely NOT! > > >> $partial = (closure) xyz(?,24); > > > > Mind if I ask for you to elaborate on your aversion? > > Asking for general understanding, not to debate the point. > > -Mike >
Sorry I was too "raw". I mean that the cast syntax `$p = (whatever) f();` already very clearly means/does "evaluate f() [i.e. call it], then convert the evaluation result to whatever, then assign the conversion result to $p", and I think it would be a **very bad idea** to "reuse" the same syntax for something that wouldn't call f() immediately. I'm favorable to other syntaxes, but which don't look like a cast. Regards,
-- Guilliam Xavier

Rowan Collins

5 years ago
On 15 May 2021 00:09:41 BST, Paul Crovella <paul.crovella@gmail.com> wrote:
>I think this highlights where the misunderstanding of this feature is.
I think the fact that there is so much confusion highlights why it is worth considering different designs.
>Partial application is about binding arguments. ? isn't an argument, >it's an argument placeholder. It does two things: signals to create a >closure wrapping the function rather than calling it immediately, and >holds a position in the argument list so that an argument further to >the right can be fixed (bound) at that time.
This is not a correct description of the current syntax. Currently, "?" represents a *required* argument in the argument list, but *only* if there is a fixed value to its right. If it appears at the end of the argument list, or with only other ? tokens to its right, it *only* signals that a partial should be created, and doesn't create a required argument, even though it looks the same. foo(?, 42) creates a closure with one required argument; foo(42, ?) creates a closure with no required arguments
>Requiring additional trailing argument placeholders or adding an >additional token `...?` unnecessarily complicates things, burdens the >user, and only serves to further promote misunderstanding.
On the contrary, saying that "?" always means exactly one argument massively simplifies the description of the feature. Why persist with a version of the syntax that is so easy to misunderstand when we have a really simple fix available? I acknowledge the need for a syntax to say "accept zero or more further arguments", but this doesn't need to overload the syntax for "create a required argument here". If the suggestion of ...? is too long, we could look at other options like ... or ?? The syntax for "just make a closure and pass all arguments through" would then be "foo(...)" or "foo(??)". There is a *separate* question of whether arguments that weren't *required* are passed along anyway. I'm less sure there's a right answer there, and would be happy with a version where foo(?, 42) and foo(?, 42, ...) were equivalent - that is, the trailing "all other arguments" token would only be needed if there wasn't already a placeholder. Regards,
-- Rowan Tommins [IMSoP]

Mike Schinkel

5 years ago
> On May 15, 2021, at 4:20 AM, Rowan Tommins <rowan.collins@gmail.com> wrote: > > On 15 May 2021 00:09:41 BST, Paul Crovella <paul.crovella@gmail.com> wrote: >> I think this highlights where the misunderstanding of this feature is. > > > I think the fact that there is so much confusion highlights why it is worth considering different designs.
Exactly this. I have been debating whether to comment on this thread, but seeing Rowen's comments mean I am not alone in my opinion so here goes. I too have been bothered with the inconsistent usage of the single question mark ('?'). While it makes perfect sense once it is explained, it is possibly not intuitive for those who have learned how to use a command line shell where a single question mark indicates a single "thing" (where "thing" is a single character in the shell.) Using it in PHP to mean "one, or more" seems like it would cause needless confusion given that intuition derived from command line experience might lead developers to be confused. Better to use something which would not be likely to lead developers astray I would think.
> >> Partial application is about binding arguments. ? isn't an argument, >> it's an argument placeholder. It does two things: signals to create a >> closure wrapping the function rather than calling it immediately, and >> holds a position in the argument list so that an argument further to >> the right can be fixed (bound) at that time. > > > This is not a correct description of the current syntax. Currently, "?" represents a *required* argument in the argument list, but *only* if there is a fixed value to its right. If it appears at the end of the argument list, or with only other ? tokens to its right, it *only* signals that a partial should be created, and doesn't create a required argument, even though it looks the same. > > foo(?, 42) creates a closure with one required argument; foo(42, ?) creates a closure with no required arguments > >> Requiring additional trailing argument placeholders or adding an >> additional token `...?` unnecessarily complicates things, burdens the >> user, and only serves to further promote misunderstanding. > > > On the contrary, saying that "?" always means exactly one argument massively simplifies the description of the feature. Why persist with a version of the syntax that is so easy to misunderstand when we have a really simple fix available? > > I acknowledge the need for a syntax to say "accept zero or more further arguments", but this doesn't need to overload the syntax for "create a required argument here". > > If the suggestion of ...? is too long, we could look at other options like ... or ?? The syntax for "just make a closure and pass all arguments through" would then be "foo(...)" or "foo(??)".
Yes! Since we need a sigil to indicate a function be wrapped in a closure I was actually planning to propose `??` so I was pleasantly surprised to see Rowan's suggestion. Ellipses would work too, but as I think `??` would be easier to see in dense code, so I would prefer them. Which gives us:: function foo($a,$b,$c) {} $x = foo(??); // Wrap foo() in a closure which expects 3 parameters $x = foo(?, 24); // Wrap foo() in a closure which expects 2 parameters with `24` binding to `$b` $x = foo(?, 24, ??); // Same as foo(?, 24);
> There is a *separate* question of whether arguments that weren't *required* are passed along anyway. I'm less sure there's a right answer there, and would be happy with a version where foo(?, 42) and foo(?, 42, ...) were equivalent - that is, the trailing "all other arguments" token would only be needed if there wasn't already a placeholder.
Ditto, but with `??` preferred. Said another way, I am arguing exactly what Rowan arged except that I have a preference for one sigil over the other.
>
-Mike

Rowan Collins

5 years ago
On 25/04/2021 20:25, Larry Garfield wrote:
> Greetings, Internalians! > > I would like to offer for your consideration another RFC, specifically syntax for partial function application. > > https://wiki.php.net/rfc/partial_function_application
During off-list discussions, it's become clear that there are two different ways of modelling what this feature does, which is leading to a bit of misunderstanding and frustration (and some plain disagreement on which is the *better* model). At risk of getting it wrong and confusing things more, I'd like to attempt to describe the two models. The first model is that a partial application starts with an empty signature, and adds arguments to it based on placeholders. You could picture the steps like this: function foo(int $a, int $b, int $c) { /* whatever */ } $partial = foo(?, ?, 42); Step 1 - empty closure: $partial = fn() => foo(); Step 2 - 1st argument is a ? so copy that parameter from the original: $partial = fn(int $a) => foo($a); Step 3 - 2nd argument is a ? so copy from the original: $partial = fn(int $a, int $b) => foo($a, $b); Step 4 - 3rd argument is a fixed value, so don't add to signature, only to the call: fn(int $a, int $b) => foo($a, $b, 42); The second model is that a partial application starts with a *complete* signature, and splices in the *fixed* values. The steps go more like this: function foo(int $a, int $b, int $c) { /* whatever */ } $partial = foo(?, ?, 42); Step 1 - copy original signature: $partial = fn(int $a, int $b, int $c) => foo($a, $b, $c); Step 2 - 1st argument is a ? so skip over it Step 3 - 2nd argument is a ? so skip over it Step 4 - 3rd argument is a fixed value, so splice a value into the signature at that location: $partial = fn(int $a, int $b, 42) => foo($a, $b, 42); Step 5 - fixed values don't actually belong on the left-hand side: $partial = fn(int $a, int $b) => foo($a, $b, 42); For simple examples like the above, the two models are essentially equivalent, but they lead to different expectations for some features; for instance, trailing arguments and placeholders: - In the model where you're building from empty, writing foo(42, ?, ?) to mean "copy exactly two arguments" feels natural. To say "copy all arguments", or "copy all remaining arguments", you'd then need a different syntax, like foo(42, ...) or foo(42, ??). In this model, the trailing "?" in the current proposal is magic: it changes from meaning "copy exactly one argument" to "copy all remaining arguments". - In the model where you're splicing arguments into a full signature, foo(42, ?, ?) is pointless: the action is to splice in the 42, then "skip over" two arguments; but nothing happens *after* skipping over them, so you could just not mention them. No extra token is needed to copy all remaining arguments, because that already happened at the start. The only reason to use a trailing "?" at all is because foo(42) would just be a normal function call, and adding a redundant "skip over" marker in foo(42, ?) tells the compiler it's a partial. Regards,
-- Rowan Tommins [IMSoP]

Mark Randall

5 years ago
On 25/04/2021 20:25, Larry Garfield wrote:
> It includes an implementation by Joe Watkins that is already about 95% complete. (There's some edge cases he's still sorting out, but all of the typical cases should work already.) Most of the design work comes from Levi Morrison and Paul Crovella. I helped out with the tests, a few edge bits, and general instigator/nudge. :-)
Can I be a little pushy and ask for a subvote on "..." vs "...?" It _probably_ won't matter, but with ... being used as the language's universal variadic / unpack, we may use it for similar things in the years to come, and I think it makes sense to pre-empt a potential conflict or source of confusion down the line by using "...?". Just as a variadic function argument is ...$ a variadic partial closure would be ...? I do not, at this time, have a firm example of where this might become a problem, otherwise I would state it. The closest I can think of for now is that it's entirely possible that auto-capturing multiline functions may end up using: $x = function() use (...) { /* */ } Which could possibly be confusing. Not a definiative example by any stretch, but but I do get the feeling that, as we can future proof it for effectively zero cost, we should. Mark Randall