Re: [RFC] Partial Function Application for instance of non-static methods ("$this")
| From: | Larry Garfield | Date: | Fri, 06 Feb 2026 20:30:11 +0000 |
| Subject: | Re: [RFC] Partial Function Application for instance of non-static methods ("$this") | ||
| References: | 1 2 3 4 5 6 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-130039@lists.php.net to get a copy of this message | ||
On Fri, Feb 6, 2026, at 10:43 AM, Tim Düsterhus wrote:
> Hi
>
> On 1/22/26 17:41, Larry Garfield wrote:
>> More spitballing on my previous reply:
>>
>> class Test {
>> public function stuff(int $a) {}
>> }
>>
>> (Test)?->stuff(?);
>
> As mentioned in the sibling mail, this is existing syntax and thus
> doesn't work.
Sure, but we can fiddle with the details to find something that works. I also suggested something
like these to Arnaud off-list:
(?: Test)->stuff(?);
(Test ?)->stuff(?);
((Test)?)->stuff(?);
> Keeping full type information is the main benefit of PFA over “just
> write a Closure”. Being able to reorder parameters as part of partial
> application is another explicit feature that would not be supported by
> that syntax.
Honestly, I don't much care about the reordering. And with this approach you'd still be
able to reorder the method params, just keep the object as first arg.
>> 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.
>
> The use case for partially applying
$this is in cases where you
> need a
> “function handle”, that's why the examples are ones where the resulting
> Closure is passed as a parameter to another function.
>
> Within a pipe chain you would just use the regular ->
> operator on the
> result of the previous step:
>
> $result = (trim($username)
> |> $repository->findBy(name: ?)
> )->getId();
>
> It would also naturally support ?-> in case your repository
> returns
> null when the user cannot be found.
Directly in a pipe chain, sure. However, I would see this as most useful as a callback to array_map
et al (or future equivalents).
Eg:
import_stuff()
|> array_filter(?, (?)->hasField('comment')
|> array_map((?)->save(), $records)
;
In this case, the only thing being partialed is the object on which to invoke. So there's no
need to think about reordering the params in the first place: There's just one.
Needing to specify the type in this case:
import_stuff()
|> array_filter(?, Record::hasField('comment', ...)
|> array_map(Record::save(...), $records)
;
Just adds more noise, and IMO creates confusion between static and non-static methods.
--Larry Garfield