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
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:
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
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:
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
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:
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
On Thu, Jun 10, 2021 at 4:34 PM Larry Garfield larry@garfieldtech.com
wrote:
On Wed, Jun 2, 2021 at 7:47 PM Larry Garfield larry@garfieldtech.com
wrote: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:
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.
Well I find that unexpected :/
As a cute side effect, Joe pointed out you could do this:
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
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 tofn (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
On Thu, Jun 10, 2021 at 7:32 PM Guilliam Xavier guilliam.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 tofn (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
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
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 guilliam.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