[RFC] Partial Function Application, take 2

  114694
June 2, 2021 17:45 larry@garfieldtech.com ("Larry Garfield")
Hi folks.  After much off-list discussion, iteration, and consideration, we have a new draft of PFA ready for review.

The URL is the same:

https://wiki.php.net/rfc/partial_function_application

It's a bit long because we wanted to be as precise as possible.  However, in short:

* Partial application creates a closure object you can use like any other.
* A ? indicates "exactly one required parameter here"
* A ... indicates "zero or more parameters here"
* If the pattern in the partial is compatible with the underlying function, everything "just works"
* If it's not compatible, you get an error.
* Named placeholders are not supported.
* Splat (argument unpacking) is not supported.

That gives us the 3 most important use cases, plus many others:

arbitrary_function(...) works to reference any function safely.  (This is what Nikita's mini-scoped proposal did.)

some_func(1, 2, ?, 5) works to reduce any function to single-argument, which makes it useful in many callback situations.

any_func($all, $params, ...) works to provide all the arguments to a function but not call it yet; you can then cal the resulting closure later with no arguments to actually invoke it.

We're reasonably confident that we have all the ins and outs covered, and in a performant way.  Because it's such a heavy rework, though, we want to give it enough time for the discussion to settle, so won't call a vote for at least 2 weeks from today (give or take the feedback we get here).

Cheers!

-- 
  Larry Garfield
  larry@garfieldtech.com
  114696
June 2, 2021 20:16 mike@newclarity.net (Mike Schinkel)
> On Jun 2, 2021, at 1:45 PM, Larry Garfield <larry@garfieldtech.com> wrote: > > Hi folks. After much off-list discussion, iteration, and consideration, we have a new draft of PFA ready for review. > > The URL is the same: > > https://wiki.php.net/rfc/partial_function_application
Really excellent work, all!
> It's a bit long because we wanted to be as precise as possible. However, in short: > > * Partial application creates a closure object you can use like any other. > * A ? indicates "exactly one required parameter here" > * A ... indicates "zero or more parameters here"
My only comment/request/suggestion is to consider Mark Randall's suggestion to use `...?` instead, for the reasons he mentioned in his email to the list: https://externals.io/message/114157#114666 <https://externals.io/message/114157#114666> Plus Levi Morrison seemed to approve: https://externals.io/message/114157#114667 <https://externals.io/message/114157#114667> -Mike
  114772
June 7, 2021 19:50 bjorn.x.larsson@telia.com (=?UTF-8?Q?Bj=c3=b6rn_Larsson?=)
Den 2021-06-02 kl. 22:16, skrev Mike Schinkel:
>> On Jun 2, 2021, at 1:45 PM, Larry Garfield <larry@garfieldtech.com> wrote: >> >> Hi folks. After much off-list discussion, iteration, and consideration, we have a new draft of PFA ready for review. >> >> The URL is the same: >> >> https://wiki.php.net/rfc/partial_function_application > > Really excellent work, all! > >> It's a bit long because we wanted to be as precise as possible. However, in short: >> >> * Partial application creates a closure object you can use like any other. >> * A ? indicates "exactly one required parameter here" >> * A ... indicates "zero or more parameters here" > > My only comment/request/suggestion is to consider Mark Randall's suggestion to use `...?` instead, for the reasons he mentioned in his email to the list: > > https://externals.io/message/114157#114666 <https://externals.io/message/114157#114666> > > Plus Levi Morrison seemed to approve: > > https://externals.io/message/114157#114667 <https://externals.io/message/114157#114667> > > -Mike >
I second this opinion. So is the "...?" syntax something that will be considered? r//Björn Larsson
  114824
June 10, 2021 19:41 j.boggiano@seld.be (Jordi Boggiano)
On 07/06/2021 21:50, Björn Larsson wrote:
> Den 2021-06-02 kl. 22:16, skrev Mike Schinkel: >> My only comment/request/suggestion is to consider Mark Randall's >> suggestion to use `...?` instead, for the reasons he mentioned in his >> email to the list: >> >> https://externals.io/message/114157#114666 >> <https://externals.io/message/114157#114666> >> >> Plus Levi Morrison seemed to approve: >> >> https://externals.io/message/114157#114667 >> <https://externals.io/message/114157#114667> >> >> -Mike >> > > I second this opinion. So is the "...?" syntax something that will > be considered? > As this was still not answered.. I would like to just drop a +1. I do
find the symmetry of "...?" / "?" quite appealing too, ensuring that you always get a "?" to indicate a partial application. If it's controversial, Mark Randall's idea of having it be a subvote sounds good to me to make sure the debate is closed once and for all. Ignoring it now to get another RFC trying to change the syntax in two months isn't helpful :) Aside from that, also +1 on "good work everyone involved", this looks pretty solid now and I'm looking forward to the addition! Best, Jordi -- Jordi Boggiano @seldaek - https://seld.be
  114808
June 10, 2021 08:17 guilliam.xavier@gmail.com (Guilliam Xavier)
On Wed, Jun 2, 2021 at 7:47 PM Larry Garfield <larry@garfieldtech.com>
wrote:

> Hi folks. After much off-list discussion, iteration, and consideration, > we have a new draft of PFA ready for review. > > The URL is the same: > > https://wiki.php.net/rfc/partial_function_application >
Hi, thanks all for the reworks! I just thought to something: does it support nullsafe calls, e.g. `$foo?->bar(?)`? and if yes, what is the signature of the Closure when `$foo === null` (and does it make a difference if, inside a function, it was created as a local variable `$foo = null;` vs received as a typed parameter `?Foo $foo`)? Related, for `$null === null`, is `$c = $null->bar(?);` / `$c = $null::baz(?);` an immediate error, or only later when calling `$c($arg)`? Regards, -- Guilliam Xavier
  114813
June 10, 2021 14:33 larry@garfieldtech.com ("Larry Garfield")
On Thu, Jun 10, 2021, at 3:17 AM, Guilliam Xavier wrote:
> On Wed, Jun 2, 2021 at 7:47 PM Larry Garfield <larry@garfieldtech.com> > wrote: > > > Hi folks. After much off-list discussion, iteration, and consideration, > > we have a new draft of PFA ready for review. > > > > The URL is the same: > > > > https://wiki.php.net/rfc/partial_function_application > > > > Hi, thanks all for the reworks! I just thought to something: does it > support nullsafe calls, e.g. `$foo?->bar(?)`? and if yes, what is the > signature of the Closure when `$foo === null` (and does it make a > difference if, inside a function, it was created as a local variable `$foo > = null;` vs received as a typed parameter `?Foo $foo`)?
I just checked, and it... sort of supports nullsafe. Rather, if you try to partial a method on a null object, you get null back for your closure. Then when you try to call it, you get a "cannot invoke null" error. Which... I think makes sense. cf: https://3v4l.org/4XeB3/rfc#focus=rfc.partials As a cute side effect, Joe pointed out you could do this: https://3v4l.org/ERRdY/rfc#focus=rfc.partials I wouldn't really call that a feature, but more of a side effect of the implementation. Please don't count on that behavior. I also discovered while checking that nullsafe differentiates between null and undefined. That was unexpected, but not related to the topic at hand. :-)
> Related, for `$null === null`, is `$c = $null->bar(?);` / `$c = > $null::baz(?);` an immediate error, or only later when calling `$c($arg)`?
That dies with "call to member function on null" when trying to create the partial: https://3v4l.org/E6MgK/rfc#focus=rfc.partials --Larry Garfield
  114821
June 10, 2021 17:32 guilliam.xavier@gmail.com (Guilliam Xavier)
On Thu, Jun 10, 2021 at 4:34 PM Larry Garfield <larry@garfieldtech.com>
wrote:

> On Thu, Jun 10, 2021, at 3:17 AM, Guilliam Xavier wrote: > > On Wed, Jun 2, 2021 at 7:47 PM Larry Garfield <larry@garfieldtech.com> > > wrote: > > > > > https://wiki.php.net/rfc/partial_function_application > > > > for `$null === null`, is `$c = $null->bar(?);` / `$c = > > $null::baz(?);` an immediate error, or only later when calling > `$c($arg)`? > > That dies with "call to member function on null" when trying to create the > partial: > > https://3v4l.org/E6MgK/rfc#focus=rfc.partials >
That makes sense (eager evaluation of non-placeholder "arguments", including the called-on object).
> > does it > > support nullsafe calls, e.g. `$foo?->bar(?)`? and if yes, what is the > > signature of the Closure when `$foo === null`? > > I just checked, and it... sort of supports nullsafe. Rather, if you try > to partial a method on a null object, you get null back for your closure. > Then when you try to call it, you get a "cannot invoke null" error. > Which... I think makes sense. > > cf: https://3v4l.org/4XeB3/rfc#focus=rfc.partials
Well I find that unexpected :/
> As a cute side effect, Joe pointed out you could do this: > > https://3v4l.org/ERRdY/rfc#focus=rfc.partials
So there's a "workaround", although I wouldn't call it precisely "cute"...
> I wouldn't really call that a feature, but more of a side effect of the > implementation. Please don't count on that behavior. >
I'd rather not indeed ;) Since `$null?->whatever(1, 'a')` currently always returns null without error, shouldn't `$null?->whatever(?, 'a')` return a closure (with a signature built from the placeholders only) that will return null when called (i.e. equivalent to `fn (mixed $arg1) => null` here)? Thanks, -- Guilliam Xavier
  114822
June 10, 2021 18:34 Danack@basereality.com (Dan Ackroyd)
On Thu, 10 Jun 2021 at 18:32, Guilliam Xavier xavier@gmail.com> wrote:
> Since `$null?->whatever(1, 'a')` currently always > returns null without error, shouldn't `$null?->whatever(?, 'a')` return a > closure (with a signature built from the placeholders only) that will > return null when called (i.e. equivalent to `fn (mixed $arg1) => null` > here)?
No. The short circuiting should happen in the same place, no matter what is to the right of the "?->". Having the behaviour vary and sometimes not return null, would be highly surprising. cheers Dan Ack
  114823
June 10, 2021 18:35 guilliam.xavier@gmail.com (Guilliam Xavier)
On Thu, Jun 10, 2021 at 7:32 PM Guilliam Xavier xavier@gmail.com>
wrote:

> > Since `$null?->whatever(1, 'a')` currently always returns null without > error, shouldn't `$null?->whatever(?, 'a')` return a closure (with a > signature built from the placeholders only) that will return null when > called (i.e. equivalent to `fn (mixed $arg1) => null` here)? >
Ah, we can also see `$foo?->bar(?)` as simply "desugaring" to `$foo === null ? null : $foo->bar(?)`, so the current behavior makes sense too (and maybe even "more")... I guess I was confused to get a "null-implying error" despite using a "null-safe" call syntax :s (There's also the possibility of not supporting it, but I guess that would be a lose-lose...) -- Guilliam Xavier
  114828
June 11, 2021 11:01 guilliam.xavier@gmail.com (Guilliam Xavier)
Sorry, me again :s  I have tested the examples from
https://wiki.php.net/rfc/partial_function_application on
https://3v4l.org/#focus=rfc.partials and several of them currently give an
error:

- Ex 10: on the line `$c = stuff(?, ?, f: 3.5, ..., p: $point);`
=> Fatal error: Named arguments must come after all place holders
(typo I guess, `$c = stuff(?, ?, ..., f: 3.5, p: $point);` is OK)

- (Ex 11: no error but a typo: `'hi'` vs `'foo'`)

- Ex 16: for the last call `(four(..., d: 4, a: 1))(2, 3);`
=> Fatal error: Uncaught ArgumentCountError: four(): Argument #2 ($b) not
passed
(on the function definition line)
(`(four(..., d: 4, a: 1))(2, 3, 5, 6);` idem,
but `(four(..., d: 4, a: 1))(b: 2, c: 3);` throws an "Unknown named
parameter $b" Error on the call line)
(weird)

- func_get_args() and friends: one the last line `$f(1, 2);` (after `$f =
f(?);`)
=> Fatal error: Uncaught Error: too many arguments for application of f, 2
given and a maximum of 1 expected
(can make sense, e.g. https://externals.io/message/114532#114554 )

- (Callable reference: no error but a typo: `$f` vs `$foo`)

- Optimizations: on the line `$boo = $baz(4, ...);`
=> Fatal error: Uncaught Error: too many arguments and or place holders for
application of Closure::__invoke, 1 given and a maximum of 0 expected
(`$boo = $baz(?);` throws the same error,
but `$boo = $baz(4);` throws a "not enough arguments for implementation of
foo, 4 given and exactly 5 expected" Error,
and `$boo = $baz(...);` makes the subsequent `$boo(5);` throw a "not enough
arguments ..." Error)
(weird, looks like `$bar = $foo(2, ...);` and/or `$baz = $bar(3, ...);`
dropped too many params)

Regards,

-- 
Guilliam Xavier
  114830
June 11, 2021 12:41 krakjoe@gmail.com (Joe Watkins)
There's a couple of typos in the RFC, which Larry will fix when he has time.

There was also a typo in patch, and a fault in patch.

All fixed. pending tests, 3v4l won't update until tomorrow.

Cheers
Joe

On Fri, 11 Jun 2021 at 13:02, Guilliam Xavier xavier@gmail.com>
wrote:

> Sorry, me again :s I have tested the examples from > https://wiki.php.net/rfc/partial_function_application on > https://3v4l.org/#focus=rfc.partials and several of them currently give an > error: > > - Ex 10: on the line `$c = stuff(?, ?, f: 3.5, ..., p: $point);` > => Fatal error: Named arguments must come after all place holders > (typo I guess, `$c = stuff(?, ?, ..., f: 3.5, p: $point);` is OK) > > - (Ex 11: no error but a typo: `'hi'` vs `'foo'`) > > - Ex 16: for the last call `(four(..., d: 4, a: 1))(2, 3);` > => Fatal error: Uncaught ArgumentCountError: four(): Argument #2 ($b) not > passed > (on the function definition line) > (`(four(..., d: 4, a: 1))(2, 3, 5, 6);` idem, > but `(four(..., d: 4, a: 1))(b: 2, c: 3);` throws an "Unknown named > parameter $b" Error on the call line) > (weird) > > - func_get_args() and friends: one the last line `$f(1, 2);` (after `$f = > f(?);`) > => Fatal error: Uncaught Error: too many arguments for application of f, 2 > given and a maximum of 1 expected > (can make sense, e.g. https://externals.io/message/114532#114554 ) > > - (Callable reference: no error but a typo: `$f` vs `$foo`) > > - Optimizations: on the line `$boo = $baz(4, ...);` > => Fatal error: Uncaught Error: too many arguments and or place holders for > application of Closure::__invoke, 1 given and a maximum of 0 expected > (`$boo = $baz(?);` throws the same error, > but `$boo = $baz(4);` throws a "not enough arguments for implementation of > foo, 4 given and exactly 5 expected" Error, > and `$boo = $baz(...);` makes the subsequent `$boo(5);` throw a "not enough > arguments ..." Error) > (weird, looks like `$bar = $foo(2, ...);` and/or `$baz = $bar(3, ...);` > dropped too many params) > > Regards, > > -- > Guilliam Xavier >