Re: [RFC] Partial Function Application for instance of non-static methods ("$this")

From: Date: Fri, 06 Feb 2026 23:21:50 +0000
Subject: Re: [RFC] Partial Function Application for instance of non-static methods ("$this")
References: 1 2 3 4 5 6 7  Groups: php.internals 
Request: Send a blank email to internals+get-130041@lists.php.net to get a copy of this message
On Friday, February 6th, 2026 at 21:32, Larry Garfield <larry@garfieldtech..com> wrote: > 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 > I find the (Test ?)->method() suggestion quite nice. For 2 reasons, it looks similar to a function argument ("Type $arg"), and following that, given that the type is optional for function arguments, it could also be optional here to have (?)->method() or even ?->method() as a shortcut. I understand it means some optimization cannot be done, but in a lot of case it doesn't matter, and if I have to write DateTimeImmutable::format($this: ?, 'c') vs fn ($dt) => $dt->format('c'). The only benefit would be the optimization. so if I don't care about that I'm still going to go for the shorter one unless this a performance critical situation Having (DateTimeImmutable ?)->format('c') and ?->format('c') as a non-optimized shortcut seems more interesting to me. I'm not too bothered by the reordering, I think this still covers 99% of the use cases --- That said, regarding your 2 suggestions, Tim, and after reading your explanations, my preference goes for $this: ? And yet more suggestions: DateTimeImmutable::methodName($: ?, ...) and whatever is between $ and : could be used as the arg name of the generated function (with something like "__this" being the default if omitted) the $ prefix could also be replaced by any symbol not authorized at the start of an argument name, eg: * DateTimeImmutable::methodName(*it: ?, ...) * DateTimeImmutable::methodName(~it: ?, ...) * DateTimeImmutable::methodName(.it: ?, ...) (it's getting worse and worse, ain't it?) or, even more drastic changes: * DateTimeImmutable::methodName(*it, ...) * DateTimeImmutable::methodName(?this, ...) (feel free to silently ignore them; I'm not putting that much thought into those suggestions) regards, Mathieu Rochette

Attachment: [application/pgp-keys] publickey - mathieu@rochette.cc - 0x8D30BA0E.asc
Attachment: [application/pgp-signature] OpenPGP digital signature signature.asc
« previous php.internals (#130041) next »