Hi
Arnaud and I would like to start discussion on our RFC “Partial Function
Application for instance of non-static methods ("$this")” which is
intended to round-off the Partial Function Application RFC that was
accepted recently:
https://wiki.php.net/rfc/partial_function_application_this
Best regards
Tim Düsterhus
Arnaud and I would like to start discussion on our RFC “Partial Function
Application for instance of non-static methods ("$this")” which is intended to
round-off the Partial Function Application RFC that was accepted recently:
We propose allowing a named parameter $this: ? to be combined with the
“scope resolution operator” syntax of referring to a method.
I can't say I am entirely sold on the syntax here — starting with a $
makes it look like a variable.
Since the $this: ? parameter does not refer to a parameter in
the underlying argument list and thus there is no ambiguity with
regard to positional parameters it may be placed at any position
of the parameter list
I think I would be keen on requiring this to be the first in the
argument list.
Although there is no ambiguity, I also can't see why you would want to
have it anywhere else.
And the RFC does acknowledge "instance methods which effectively carry
an implicit $this parameter that is provided on the left side of the ->
operator."
With both these two above comments, I think I would like to see a better
syntax — but I am unsure if that's possible.
It may also not be used when partially applying an instance
method with a known object.
$c = $dateTime->format($this: ?, "c"); // Fatal error: Invalid use of $this placeholder
Can you explain (in the RFC) what you mean by "known object" — I don't
think the language on what this means is clear enough.
cheers,
Derick
Hi
Am 2026-01-22 16:33, schrieb Derick Rethans:
We propose allowing a named parameter $this: ? to be combined with the
“scope resolution operator” syntax of referring to a method.I can't say I am entirely sold on the syntax here — starting with a $
makes it look like a variable.
Yes, it certainly requires some getting used to it. It's the best we
could come up with, but if there are any better suggestions, we're open
to hearing those. I'd like to note the “Rejected Features” section,
since it lists some non-workable alternatives we considered.
Since the $this: ? parameter does not refer to a parameter in
the underlying argument list and thus there is no ambiguity with
regard to positional parameters it may be placed at any position
of the parameter listI think I would be keen on requiring this to be the first in the
argument list.Although there is no ambiguity, I also can't see why you would want to
have it anywhere else.
Being able to “reorder” parameters to (better) fit a specific callback
signature is an explicit feature of PFA that we also wanted to support
for $this.
As an example, suppose objects could be used as an array key.
array_find() first passes the value and then the key to the callback.
In this case users might want to do the following:
class SomeObject {
public function __construct(private string $value) { }
public function hasMatchingValue(string $value) { return $value
=== $this->value; }
}
$array = [
new SomeObject('foo') => 'bar',
new SomeObject('baz') => 'baz',
];
array_find(SomeObject::hasMatchingValue(value: ?, $this: ?),
$array);
In this case array_find() is supposed to return 'baz', since this is
where the values match.
It may also not be used when partially applying an instance
method with a known object.$c = $dateTime->format($this: ?, "c"); // Fatal error: Invalid use of
$this placeholderCan you explain (in the RFC) what you mean by "known object" — I don't
think the language on what this means is clear enough.
I have rephrased that to “It may also not be used when partially
applying an instance method using the -> operator, since the object is
already specified:”
Best regards
Tim Düsterhus
Yes, it certainly requires some getting used to it. It's the best we
could come up with, but if there are any better suggestions, we're
open to hearing those. I'd like to note the “Rejected Features”
section, since it lists some non-workable alternatives we considered.
Looking at this:
PHP itself already has "ALGOL-style" declarations, for "const",
"global", and "static".
I'm reminded of placeholders in PostgreSQL; the type is needed to choose
overloaded functions and operators, so this doesn't compile (without
extra metadata):
Select do_something(?) as result
Instead, you can insert a dummy cast to indicate the type:
Select do_something(?::int) as result
The PHP equivalent would be something like this:
$f = (DateTimeImmutable)?->format('c');
Or, slightly more readable but less consistent with casts:
$f = (? as DateTimeImmutable)->format('c');
That doesn't solve the second part of the problem though:
It would also not allow to reorder the parameters in the resulting
Closure, which is an explicit feature of Partial Function Application.
I thought I'd throw it out there anyway in case it stimulates any other
ideas.
--
Rowan Tommins
[IMSoP]
Yes, it certainly requires some getting used to it. It's the best we
could come up with, but if there are any better suggestions, we're
open to hearing those. I'd like to note the “Rejected Features”
section, since it lists some non-workable alternatives we considered.Looking at this:
PHP itself already has "ALGOL-style" declarations, for "const",
"global", and "static".I'm reminded of placeholders in PostgreSQL; the type is needed to choose
overloaded functions and operators, so this doesn't compile (without
extra metadata):Select do_something(?) as result
Instead, you can insert a dummy cast to indicate the type:
Select do_something(?::int) as result
The PHP equivalent would be something like this:
$f = (DateTimeImmutable)?->format('c');
Or, slightly more readable but less consistent with casts:
$f = (? as DateTimeImmutable)->format('c');
That doesn't solve the second part of the problem though:
It would also not allow to reorder the parameters in the resulting
Closure, which is an explicit feature of Partial Function Application.I thought I'd throw it out there anyway in case it stimulates any other
ideas.
More spitballing on my previous reply:
class Test {
public function stuff(int $a) {}
}
(Test)?->stuff(?);
Translates to:
fn(Test $__this, int $a) => $__this->stuff($a);
But if there's no partialing on the method side, it could get abbreviated to:
(?)->stuff(4);
Translates to:
fn(object $__this) => $__this->stuff(4);
Because we don't need to know the type at compile time, and a method-not-found error would still happen at runtime anyway if necessary. That would then be a lot easier to write in cases where you're just dropping a $this->stuff() call into a pipe chain but want to receive $this.
I'm not sure if that would also allow for reading properties with the same syntax?
--Larry Garfield
Hi
Am 2026-01-22 17:26, schrieb Rowan Tommins [IMSoP]:
The PHP equivalent would be something like this:
$f = (DateTimeImmutable)?->format('c');
See my reply to Mathieu on why this specific one wouldn't work.
Or, slightly more readable but less consistent with casts:
$f = (? as DateTimeImmutable)->format('c');
That would work, but indeed would not solve the reordering case. I also
feel it is inventing more new syntax than the current
“scope-resolution-operator” syntax the RFC uses (which is not just used
for static method calls).
Best regards
Tim Düsterhus
Hi
Am 2026-01-22 16:33, schrieb Derick Rethans:
We propose allowing a named parameter $this: ? to be combined with the
“scope resolution operator” syntax of referring to a method.I can't say I am entirely sold on the syntax here — starting with a $
makes it look like a variable.Yes, it certainly requires some getting used to it. It's the best we
could come up with, but if there are any better suggestions, we're open
to hearing those. I'd like to note the “Rejected Features” section,
since it lists some non-workable alternatives we considered.
I too am very much in favor of this functionality, though not a fan of the syntax either.
My previous, un-finished thinking was for $$->foo('blah'), which would translate to a mixed fn that just forwards the call. I... hadn't actually thought about also partially applying the method, since the use cases I can think of would all be "call this method on the value piped from the previous pipe step." (Or, read this property.) That would allow it to be embedded within an array_map() call or similar, since it's technically independent of both PFA and pipes.
If it's being partially applied, then I don't see a way around the redundant typing, which is annoying. At least not without vastly better type inference than we have today.
So, I think I am mainly interested in the degenerate case where $this is the only thing being partially applied, which seems like a common enough case that it could have a nicer version? I'm just spitballing at this point, I think. But I do like the direction.
--Larry Garfield
Hi
Arnaud and I would like to start discussion on our RFC “Partial Function
Application for instance of non-static methods ("$this")” which is
intended to round-off the Partial Function Application RFC that was
accepted recently:
Hi!
I often whished something like this existed :) Didn't get trough to suggest it because I wasn't sure of the syntax either (and lazyness)
still, my syntax suggestions:
array_map(DateTimeImmutable->format("c"), $dates);
I understand this doesn't allow reordeing though, but it feels so much better and I expect would be what's needed for most cases that I hope something like this could work.
and an opt-in mechanic to reorder could be introduced for that use case. the previous suggestion could be a shortcut for:
array_map(?::DateTimeImmutable->format("c"), $dates);
and a separate improvement to PFA could add support for picking arguments by name or position:
array_find(?key::SomeObject->hasMatchingValue(?), $array);
array_find(?2::SomeObject->hasMatchingValue(?), $array);
I noticed ?->method(?) was considered; evne if it was possible, I would prefer if the type was explict, as you may want to use an interface name (or a union type, etc.) too
regards,
Mathieu Rochette
Best regards
Tim Düsterhus
Hi
Am 2026-01-22 18:30, schrieb Mathieu Rochette:
I often whished something like this existed :) Didn't get trough to
suggest it because I wasn't sure of the syntax either (and lazyness)
Thank you for participating in the discussion of this RFC then. Having
more voices available helps building things that suit the community best
:-)
still, my syntax suggestions:
array_map(DateTimeImmutable->format("c"), $dates);
Unfortunately this doesn't work, because it is a already-legal method
call on a method stored in the constant called DateTimeImmutable. See:
https://3v4l.org/3khkT#veol
I noticed
?->method(?)was considered; evne if it was possible, I
would prefer if the type was explict, as you may want to use an
interface name (or a union type, etc.) too
Union types would likely be unsupported either way, since multiple
members of the union could have incompatible signatures for the same
method. Interfaces are fully supported with the currently proposed
syntax (it's explicitly mentioned in the RFC).
Best regards
Tim Düsterhus
Mathieu Rochette
Hi
Am 2026-01-22 18:30, schrieb
Mathieu Rochette
:
I often whished something like this existed :) Didn't get trough to
suggest it because I wasn't sure of the syntax either (and lazyness)
Thank you for participating in the discussion of this RFC then. Having
more voices available helps building things that suit the community best
:-)
still, my syntax suggestions:
array_map(DateTimeImmutable->format("c"), $dates);
Unfortunately this doesn't work, because it is a already-legal method
call on a method stored in the constant calledDateTimeImmutable. See:
https://3v4l.org/3khkT#veol
ohhh
then, continuing on bikeshedding, I think I still prefer ?::DateTimeImmutable->format("c") or (? as DateTimeImmutable)->format("c") as Larry mentioned.
(DateTimeImmutable)?->format("c") feels a bit weird. Reminds me too much of the null-safe operator.
I noticed
?->method(?)was considered; evne if it was possible, I
would prefer if the type was explict, as you may want to use an
interface name (or a union type, etc.) too
Union types would likely be unsupported either way, since multiple
members of the union could have incompatible signatures for the same
They could have incompatible signatures, yes, but they also could have compatible ones too. It's not a deal breaker. I don't imagine needing this in practice. But I don't see why the language would prevent that; I can already make that mistake with short closures or any methods. For me, it's the role of the static analyzer to tell me if I made such a mistake (or runtime errors ^^).
Interfaces are fully supported with the currently proposed
syntax (it's explicitly mentioned in the RFC).
Best regards
Tim Düsterhus
Hi
Am 2026-01-23 14:06, schrieb Mathieu Rochette:
(DateTimeImmutable)?->format("c")feels a bit weird. Reminds me too
much of the null-safe operator.
That's because it is. It's literally the same case of a
DateTimeImmutable constant with useless parentheses and the null-safe
object member access: https://3v4l.org/NebOR#veol
They could have incompatible signatures, yes, but they also could have
compatible ones too. It's not a deal breaker. I don't imagine needing
this in practice. But I don't see why the language would prevent that;
I can already make that mistake with short closures or any methods. For
me, it's the role of the static analyzer to tell me if I made such a
mistake (or runtime errors ^^).
The signature would need to be “unique” to properly generate the
signature of the resulting Closure.
Best regards
Tim Düsterhus
Arnaud and I would like to start discussion on our RFC “Partial Function
Application for instance of non-static methods ("$this")” which is
intended to round-off the Partial Function Application RFC that was
accepted recently:
Hi Tim and Arnaud,
Thank you for your work on this. However, I'm struggling to see the benefit
of this syntax over short closures given the examples in the RFC:
$dates = [
new DateTimeImmutable('now'),
];
$formattedDates = array_map(DateTimeImmutable::format($this: ?, "c"), $dates);
This saves only three characters compared to a short closure:
$formattedDates = array_map(fn(DateTimeImmutable $d) => $d->format("c"), $dates);
Furthermore, duplicating the array's type isn't necessary when IDEs and static
analysis can infer it, so short closures can usually be even more concise:
$formattedDates = array_map(fn($d) => $d->format("c"), $dates);
I also find the use of $this as a named argument confusing. It makes it
appear as though the current class instance is somehow being passed to the
method, when this isn't the case.
The other example with Symfony Form's ChoiceType doesn't sell the syntax
for me, either:
'choice_label' => Category::getName($this: ?),
A short closure is barely any longer, and makes it easier to add additional
logic when needed:
'choice_label' => fn(Category $c) => $c->getName(),
'choice_label' => fn(Category $c) => strtoupper($c->getName()),
Ultimately I'm not convinced it's worth it to make the partial function syntax
and semantics more complicated to support this scenario, given that short
closures already provide similar conciseness and greater flexibility.
Kind regards,
Theodore
Hi
Am 2026-01-23 00:25, schrieb Theodore Brown:
Thank you for your work on this. However, I'm struggling to see the
benefit
of this syntax over short closures given the examples in the RFC:$dates = [ new DateTimeImmutable('now'), ]; $formattedDates = array_map(DateTimeImmutable::format($this: ?,"c"), $dates);
To provide some background on how this RFC came about: Arnaud and I were
discussing my recent array_map() optimization
(https://github.com/php/php-src/pull/20934) that compiles array_map()
calls that either use FCC or PFA as the callback into the equivalent
foreach() loop. The optimization skipped when an actual Closure is
passed, since full-blown Closures come with all kinds of scope-related
(edge) cases to consider, making it much more complicated. I noted that
I believed that with PFA many array_map() cases could be handled
without a full Closure and that it was unfortunate that it didn't work
for “filling in the object itself”.
I then came up with the basic idea for the proposed syntax half-a-minute
later and we figured it would at least be worth proposing it, since it
mapped onto the existing PFA semantics in a fairly straight-forward
fashion.
This saves only three characters compared to a short closure:
$formattedDates = array_map(fn(DateTimeImmutable $d) =>$d->format("c"), $dates);
The return type would be missing in that snippet. And depending on your
codebase's policy you might also need static in front of the short
Closure. This might become less relevant with
https://news-web.php.net/php.internals/129825, but personally I would
still consider it a best-practice to be explicit.
Personally I also feel that short Closures have some “visual overhead”
in that they don't feel like a simple “method reference”. It's quite
similar to how PFA itself is technically redundant with short Closures,
but allows to be more explicit with the intention (which enabled the
optimization mentioned above).
Furthermore, duplicating the array's type isn't necessary when IDEs and
static
analysis can infer it, so short closures can usually be even more
concise:$formattedDates = array_map(fn($d) => $d->format("c"), $dates);
This is true when the Closure is immediately consumed, but not when it's
intended to be reused at a later point, e.g. with the Symfony Form
example where the IDE / Static Analyzer needs some deeper knowledge
about how the 2nd and 3rd parameter of ->add() interact.
I also find the use of
$thisas a named argument confusing. It makes
it
appear as though the current class instance is somehow being passed to
the
method, when this isn't the case.
That's fair. I would've hoped to just use this: without the $, but
that comes with the breaking change for variadics mentioned at the
bottom of the RFC.
A short closure is barely any longer, and makes it easier to add
additional
logic when needed:'choice_label' => fn(Category $c) => $c->getName(), 'choice_label' => fn(Category $c) => strtoupper($c->getName()),
Note: Return type + Static also missing here. And of course if more
parameters would be passed, it would scale differently (since more types
are required for proper autocompletion).
Best regards
Tim Düsterhus
Hi Tim,
Hi
Arnaud and I would like to start discussion on our RFC “Partial Function
Application for instance of non-static methods ("$this")” which is
intended to round-off the Partial Function Application RFC that was
accepted recently:
I really like that feature.
The syntax however, including some proposed in this thread, makes
little sense to me.
It is also challenging as it targets 8.6. I am not sure it is a good
idea to rush and make bad compromises with the syntax because of BC or
other issues due to the engine current state/behaviors.
I would rather wait for the next major and have a proper, self
explained syntax. Even if I doubt that "_" as function name will ever
be deprecated as well, that would be my favorite. The next would be
"it", as new keyword, $formattedDates =
array_map(DateTimeImmutable::format(it, "c"), $dates);
That could give some time to send some deprecation warning for "it",
as an example. Or whatever other symbols may be chosen. Or whatever
else would be needed to have a better approach that could last for the
next decade :).
best,
Pierre
Hey Tim,
Hi
Arnaud and I would like to start discussion on our RFC “Partial
Function Application for instance of non-static methods ("$this")”
which is intended to round-off the Partial Function Application RFC
that was accepted recently:https://wiki.php.net/rfc/partial_function_application_this
Best regards
Tim Düsterhus
I've been thinking about this RFC and I think it's a good addition.
I just have one question: Why "$this: $obj" and not "this: $obj"? The
variable in the function is "$this". Just like "$arg", which gets passed
as "arg: $value", without the extra leading dollar-sign.
(There's also no conflict, as you may not redeclare $this yourself in a
non-static method.)
When I see $<varname> before the colon, I sort of expect a dynamic
parameter name, but it's not.
Thanks,
Bob