Re: First-class callable partial application
| From: | Robert Landers | Date: | Fri, 17 Mar 2023 09:52:37 +0000 |
| Subject: | Re: First-class callable partial application | ||
| References: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-119735@lists.php.net to get a copy of this message | ||
On Thu, Mar 16, 2023 at 11:26 PM Larry Garfield <larry@garfieldtech..com> wrote:
>
> On Thu, Mar 16, 2023, at 4:14 AM, Rowan Tommins wrote:
> > On 15/03/2023 21:12, Dan Ackroyd wrote:
> >> Would it be desirable to split those two things into two separate
> >> RFCs, by having the first RFC not have native syntax support, but
> >> instead another static method on Closure? e.g. something like:
> >>
> >> Closure::partial($callable, array $position_params, array
> >> $named_params): Closure {}
> >
> >
> > Hm... now we have the first-class callable syntax, making it an instance
> > method on Closure would allow this:
> >
> > $mapFoo = array_map(...)->partial([$foo]);
> > $filterFoo = array_filter(...)->partial([1 => $foo]);
> >
> > Which could copy over the full signature, so be equivalent to this:
> >
> > $mapFoo = static fn(array ...$arrays): array => array_map($foo, ...$arrays);
> > $filterFoo = static fn(array $array, int $mode = 0): array =>
> > array_filter($array, $foo, $mode);
> >
> > While being a similar length to a much less rich version:
> >
> > $mapFoo = fn($array) => array_map($foo, $array);
> > $filterFoo = fn($array) => array_filter($array, $foo);
>
> Fascinating! I... don't know if we considered something like that or not 3 years ago.
> It's been a while.
>
> It's definitely not as nice as the integrated syntax, but it does have the advantage of
> the implementation almost certainly being rather pedestrian in comparison. That approach would
> favor left-to-right application, but not force it, which is probably sufficient.
>
> As a thought experiment, if we had that syntax and functions that were designed to be used with
> them, it would look like so:
>
> function amap(callable $c, iterable $it) { ... }
> function implode(string $sep, iterable $it) { ... }
> function length(string $s) { ... }
>
> $arr = [1, 2, 3];
>
> $a2 = amap(...)->partial(chr(...))($arr);
>
> $str = implode(...)->partial(',')($a2);
>
> Or, if combined with pipes:
>
> $size = $arr
> |> amap(...)->partial(chr(...))
> |> implode(...)->partial(',')
> |> length(...);
>
> Which... is not terrible, especially as it doesn't preclude using higher order functions
> for more control.
>
> We would likely want partial() to accept positional args (left to right) and named args. And
> I'm more than happy to say now that the capture is only by value, ever.
>
> I wonder if there's a shorter name we could use than "partial" to make it more
> readable? Or perhaps an operator that applied only to closure objects?
>
> --Larry Garfield
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: https://www.php.net/unsub.php
>
Hey Larry,
> We would likely want partial() to accept positional args (left to right) and named args.
I think this is already supported-ish. For example, here's a partial
application in user space that seems to follow all the rules. Or at
least the rules that are followed make sense.
function partial(Closure $callable, ...$args): Closure {
return static fn(...$fullArgs) => $callable(...[...$args, ...$fullArgs]);
}
https://onlinephp.io/c/9195a
It only needs a bit of edge-condition handling (like when a parameter
is filled, you probably shouldn't be able to override arbitrary
parameters).