specialize compiled partials with one placeholder

This commit is contained in:
Kacper Donat 2026-10-27 23:10:08 +01:00
parent 04dfb21916
commit db3aab644e
2 changed files with 29 additions and 0 deletions

View File

@ -82,6 +82,24 @@ class PartiallyAppliedFunction implements Decorator, Compilable
return static fn (...$args) => $callable(...$arguments, ...array_slice(array_values($args), 0, $extra));
}
// The common shape, e.g. partial(str_pad(...), _, 8): one placeholder and no room for more arguments. An
// unfilled placeholder stays in place, as below.
if (count($placeholders) === 1 && $extra === 0) {
$position = $placeholders[0];
if ($position === 0) {
$rest = array_slice($arguments, 1);
return static fn ($value = PLACEHOLDER) => $callable($value, ...$rest);
}
return static function ($value = PLACEHOLDER) use ($callable, $arguments, $position) {
$arguments[$position] = $value;
return $callable(...$arguments);
};
}
return static function (...$args) use ($callable, $arguments, $placeholders, $extra) {
$args = array_values($args);
$count = min(count($placeholders), count($args));

View File

@ -107,6 +107,17 @@ class CompileTest extends \PHPUnit\Framework\TestCase
$this->assertSame('aa', f\compile(f\curry(str_repeat(...), null, 'a'))(2, 'extra'));
}
public function testCompileSinglePlaceholderOfInternalTarget()
{
$leading = f\partial(str_pad(...), _, 3, '0', STR_PAD_LEFT);
$middle = f\partial(substr(...), 'abcdef', _, 2);
$this->assertSame('007', f\compile($leading)('7', 'extra'));
$this->assertSame('cd', f\compile($middle)(2, 'extra'));
$this->assertSame($this->outcome(fn () => $leading()), $this->outcome(fn () => f\compile($leading)()));
$this->assertSame($this->outcome(fn () => $middle()), $this->outcome(fn () => f\compile($middle)()));
}
public function testCompileWithFewerArgumentsThanPlaceholdersMatchesDecorator()
{
$partial = f\partial(str_repeat(...), _, _);