85 lines
2.4 KiB
PHP
85 lines
2.4 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2018 Kacper Donat
|
|
*
|
|
* @author Kacper "Kadet" Donat <kacper@kadet.net>
|
|
*
|
|
* Full license available in separate LICENSE file
|
|
*/
|
|
|
|
namespace Kadet\Functional;
|
|
|
|
use Kadet\Functional\Utils\CurriedFunction;
|
|
use Kadet\Functional\Utils\FixedFunction;
|
|
use Kadet\Functional\Utils\FunctionPipeline;
|
|
use Kadet\Functional\Utils\MemoizedFunction;
|
|
use Kadet\Functional\Utils\PartiallyAppliedFunction;
|
|
|
|
use function Kadet\Functional\Internal\applyWithArity;
|
|
|
|
function partial(callable $callable, ...$args): PartiallyAppliedFunction
|
|
{
|
|
return new PartiallyAppliedFunction($callable, ...$args);
|
|
}
|
|
|
|
function apply(callable $callable, ...$args)
|
|
{
|
|
try {
|
|
$reflection = reflect($callable);
|
|
|
|
return applyWithArity(
|
|
$callable,
|
|
$callable,
|
|
$reflection->isVariadic(),
|
|
$reflection->getNumberOfRequiredParameters(),
|
|
$reflection->getNumberOfParameters(),
|
|
$args
|
|
);
|
|
} catch (\ReflectionException $exception) {
|
|
// if cannot gather necessary information for intelligent application just use normal one
|
|
return $callable(...$args);
|
|
}
|
|
}
|
|
|
|
function curry(callable $callable, ?int $threshold = null, ...$args): CurriedFunction
|
|
{
|
|
return new CurriedFunction($callable, $threshold, ...$args);
|
|
}
|
|
|
|
function uncurry(CurriedFunction $curried): PartiallyAppliedFunction
|
|
{
|
|
return $curried->uncurry();
|
|
}
|
|
|
|
function pipe(...$functions)
|
|
{
|
|
return new FunctionPipeline(...$functions);
|
|
}
|
|
|
|
function fix(callable $callable, ...$args)
|
|
{
|
|
$f = new FixedFunction($callable);
|
|
return $args ? apply($f, ...$args) : $f;
|
|
}
|
|
|
|
function memoize(callable $callable)
|
|
{
|
|
return new MemoizedFunction($callable);
|
|
}
|
|
|
|
/**
|
|
* Flattens a callable into a plain closure. Compilable objects (decorators, pipelines, predicates) are compiled, so
|
|
* calling the result does not go through them, also when given as a closure of their `__invoke` (e.g. `$pipe(...)`);
|
|
* any other callable is converted with closure().
|
|
*/
|
|
function compile(callable $callable): \Closure
|
|
{
|
|
if ($callable instanceof \Closure) {
|
|
$reflection = new \ReflectionFunction($callable);
|
|
$bound = $reflection->getClosureThis();
|
|
|
|
return $bound instanceof Compilable && $reflection->getName() === '__invoke' ? $bound->compile() : $callable;
|
|
}
|
|
|
|
return $callable instanceof Compilable ? $callable->compile() : closure($callable);
|
|
} |