share apply dispatch with compiled fixed functions

This commit is contained in:
Kacper Donat 2026-10-27 22:11:15 +01:00
parent bdb49b0d78
commit 66705f3e31
2 changed files with 34 additions and 33 deletions

View File

@ -11,10 +11,10 @@ namespace Kadet\Functional\Utils;
use function Kadet\Functional\apply; use function Kadet\Functional\apply;
use function Kadet\Functional\applyWithArity;
use Kadet\Functional\Compilable; use Kadet\Functional\Compilable;
use function Kadet\Functional\compile; use function Kadet\Functional\compile;
use Kadet\Functional\Decorator; use Kadet\Functional\Decorator;
use function Kadet\Functional\partial;
use function Kadet\Functional\reflect; use function Kadet\Functional\reflect;
class FixedFunction implements Decorator, Compilable class FixedFunction implements Decorator, Compilable
@ -70,22 +70,7 @@ class FixedFunction implements Decorator, Compilable
$call = $fixed instanceof CurriedFunction ? $fixed : compile($fixed); $call = $fixed instanceof CurriedFunction ? $fixed : compile($fixed);
$self = static function (...$args) use (&$self, $fixed, $call, $variadic, $required, $all) { $self = static function (...$args) use (&$self, $fixed, $call, $variadic, $required, $all) {
array_unshift($args, $self); return applyWithArity($fixed, $call, $variadic, $required, $all, [$self, ...$args]);
switch (true) {
case $variadic:
return partial($fixed, ...$args);
case count($args) >= $all:
$applied = $call(...array_slice($args, 0, $all));
return is_callable($applied) ? apply($applied, ...array_slice($args, $all)) : $applied;
case count($args) >= $required:
return $call(...$args);
default:
return partial($fixed, ...$args);
}
}; };
return $self; return $self;

View File

@ -24,29 +24,45 @@ function apply(callable $callable, ...$args)
{ {
try { try {
$reflection = reflect($callable); $reflection = reflect($callable);
$required = $reflection->getNumberOfRequiredParameters();
$all = $reflection->getNumberOfParameters();
switch (true) { return applyWithArity(
case $reflection->isVariadic(): $callable,
return partial($callable, ...$args); $callable,
$reflection->isVariadic(),
case count($args) >= $all: $reflection->getNumberOfRequiredParameters(),
$applied = $callable(...array_slice($args, 0, $all)); $reflection->getNumberOfParameters(),
return is_callable($applied) ? apply($applied, ...array_slice($args, $all)) : $applied; $args
);
case count($args) >= $required:
return $callable(...$args);
default:
return partial($callable, ...$args);
}
} catch (\ReflectionException $exception) { } catch (\ReflectionException $exception) {
// if cannot gather necessary information for intelligent application just use normal one // if cannot gather necessary information for intelligent application just use normal one
return $callable(...$args); return $callable(...$args);
} }
} }
/**
* The argument dispatch of apply(), with the arity of $callable already known: it calls $call when there are enough
* arguments and partially applies $callable otherwise.
*
* @internal
*/
function applyWithArity(callable $callable, callable $call, bool $variadic, int $required, int $all, array $args)
{
switch (true) {
case $variadic:
return partial($callable, ...$args);
case count($args) >= $all:
$applied = $call(...array_slice($args, 0, $all));
return is_callable($applied) ? apply($applied, ...array_slice($args, $all)) : $applied;
case count($args) >= $required:
return $call(...$args);
default:
return partial($callable, ...$args);
}
}
function curry(callable $callable, ?int $threshold = null, ...$args): CurriedFunction function curry(callable $callable, ?int $threshold = null, ...$args): CurriedFunction
{ {
return new CurriedFunction($callable, $threshold, ...$args); return new CurriedFunction($callable, $threshold, ...$args);